home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 2002 September
/
PCWorld_2002-09_cd.bin
/
Software
/
Vyzkuste
/
httrack
/
httrack-3.20RC4.exe
/
{app}
/
src
/
htslib.c
< prev
next >
Wrap
C/C++ Source or Header
|
2002-07-09
|
107KB
|
4,015 lines
/* ------------------------------------------------------------ */
/*
HTTrack Website Copier, Offline Browser for Windows and Unix
Copyright (C) Xavier Roche and other contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Important notes:
- We hereby ask people using this source NOT to use it in purpose of grabbing
emails addresses, or collecting any other private information on persons.
This would disgrace our work, and spoil the many hours we spent on it.
Please visit our Website: http://www.httrack.com
*/
/* ------------------------------------------------------------ */
/* File: Subroutines */
/* Author: Xavier Roche */
/* ------------------------------------------------------------ */
// Fichier librairie .c
#include "htslib.h"
#include "htsbauth.h"
/* specific definitions */
#include "htsbase.h"
#include "htsnet.h"
#include "htsbauth.h"
#include "htsthread.h"
#include "htsnostatic.h"
#include "htswrap.h"
#include <stdio.h>
#if HTS_WIN
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/timeb.h>
#include <fcntl.h>
// pour utimbuf
#if HTS_WIN
#include <sys/utime.h>
#else
#if HTS_PLATFORM!=3
#include <utime.h>
#else
#include <utime.h>
#endif
#endif
/* END specific definitions */
// DΘbuggage de contr⌠le
#if HTS_DEBUG_CLOSESOCK
#define _HTS_WIDE 1
#endif
#if HTS_WIDE_DEBUG
#define _HTS_WIDE 1
#endif
#if _HTS_WIDE
FILE* DEBUG_fp=NULL;
#define DEBUG_W(A) { if (DEBUG_fp==NULL) DEBUG_fp=fopen("bug.out","wb"); fprintf(DEBUG_fp,":>"A); fflush(DEBUG_fp); }
#define DEBUG_W2(A) { if (DEBUG_fp==NULL) DEBUG_fp=fopen("bug.out","wb"); fprintf(DEBUG_fp,A); fflush(DEBUG_fp); }
#endif
/* variables globales */
int _DEBUG_HEAD;
FILE* ioinfo;
#if HTS_USEOPENSSL
SSL_CTX *openssl_ctx = NULL;
#endif
/* dΘtection complΘmentaire */
const char hts_detect[][32] = {
"archive",
"background",
"data", // OBJECT
"dynsrc",
"lowsrc",
"profile", // element META
"src",
"swurl",
"url",
"usemap",
"longdesc", // accessibility
"xlink:href", // xml/svg tag
""
};
/* dΘtecter dΘbut */
const char hts_detectbeg[][32] = {
"hotspot", /* hotspot1=..,hotspot2=.. */
""
};
/* ne pas dΘtcter de liens dedans */
const char hts_nodetect[][32] = {
"accept-charset",
"accesskey",
"action",
"align",
"alt",
"axes",
"axis",
"char",
"charset",
"cite",
"class",
"classid",
"code",
"color",
"datetime",
"dir",
"enctype",
"face",
"height",
"id",
"lang",
"language",
"media",
"method",
"name",
"prompt",
"scheme",
"size",
"style",
"target",
"title",
"type",
"valign",
"version",
"width",
""
};
/* dΘtection de mini-code javascript */
/* ALSO USED: detection based on the name: onXXX="<tag>" where XXX starts with upper case letter */
const char hts_detect_js[][32] = {
"onAbort",
"onBlur",
"onChange",
"onClick",
"onDblClick",
"onDragDrop",
"onError",
"onFocus",
"onKeyDown",
"onKeyPress",
"onKeyUp",
"onLoad",
"onMouseDown",
"onMouseMove",
"onMouseOut",
"onMouseOver",
"onMouseUp",
"onMove",
"onReset",
"onResize",
"onSelect",
"onSubmit",
"onUnload",
""
};
/* dΘtection "...URL=<url>" */
const char hts_detectURL[][32] = {
"content",
""
};
/* tags o∙ l'URL doit Ωtre rΘΘcrite mais non capturΘe */
const char hts_detectandleave[][32] = {
"action",
""
};
/* ne pas renommer les types renvoyΘs (couvent types inconnus) */
const char hts_mime_keep[][32] = {
"application/octet-stream",
"text/plain",
""
};
/* pas de type mime connu, mais extension connue */
const char hts_ext_dynamic[][32] = {
"php3",
"php",
"php4",
"php2",
"cgi",
"asp",
"jsp",
"pl",
/*"exe",*/
"cfm",
""
};
/* types MIME */
const char hts_mime[][2][32] = {
{"application/acad","dwg"},
{"application/arj","arj"},
{"application/clariscad","ccad"},
{"application/drafting","drw"},
{"application/dxf","dxf"},
{"application/excel","xl"},
{"application/i-deas","unv"},
{"application/iges","isg"},
{"application/iges","iges"},
{"application/mac-binhex40","hqx"},
{"application/mac-compactpro","cpt"},
{"application/msword","doc"},
{"application/msword","w6w"},
{"application/msword","word"},
{"application/mswrite","wri"},
/*{"application/octet-stream","dms"},*/
/*{"application/octet-stream","lzh"},*/
/*{"application/octet-stream","lha"},*/
/*{"application/octet-stream","bin"},*/
{"application/oda","oda"},
{"application/pdf","pdf"},
{"application/postscript","ps"},
{"application/postscript","ai"},
{"application/postscript","eps"},
{"application/powerpoint","ppt"},
{"application/pro_eng","prt"},
{"application/pro_eng","part"},
{"application/rtf","rtf"},
{"application/set","set"},
{"application/sla","stl"},
{"application/smil","smi"},
{"application/smil","smil"},
{"application/smil","sml"},
{"application/solids","sol"},
{"application/STEP","stp"},
{"application/STEP","step"},
{"application/vda","vda"},
{"application/x-authorware-map","aam"},
{"application/x-authorware-seg","aas"},
{"application/x-authorware-bin","aab"},
{"application/x-cocoa","cco"},
{"application/x-csh","csh"},
{"application/x-director","dir"},
{"application/x-director","dcr"},
{"application/x-director","dxr"},
{"application/x-mif","mif"},
{"application/x-dvi","dvi"},
{"application/x-gzip","gz"},
{"application/x-gzip","gzip"},
{"application/x-hdf","hdf"},
{"application/x-javascript","js"},
{"application/x-koan","skp"},
{"application/x-koan","skd"},
{"application/x-koan","skt"},
{"application/x-koan","skm"},
{"application/x-latex","latex"},
{"application/x-netcdf","nc"},
{"application/x-netcdf","cdf"},
/* {"application/x-sh","sh"}, */
/* {"application/x-csh","csh"}, */
/* {"application/x-ksh","ksh"}, */
{"application/x-shar","shar"},
{"application/x-stuffit","sit"},
{"application/x-tcl","tcl"},
{"application/x-tex","tex"},
{"application/x-texinfo","texinfo"},
{"application/x-texinfo","texi"},
{"application/x-troff","t"},
{"application/x-troff","tr"},
{"application/x-troff","roff"},
{"application/x-troff-man","man"},
{"application/x-troff-me","ms"},
{"application/x-wais-source","src"},
{"application/zip","zip"},
{"application/x-zip-compressed","zip"},
{"application/x-bcpio","bcpio"},
{"application/x-cdlink","vcd"},
{"application/x-cpio","cpio"},
{"application/x-gtar","tgz"},
{"application/x-gtar","gtar"},
{"application/x-shar","shar"},
{"application/x-shockwave-flash","swf"},
{"application/x-sv4cpio","sv4cpio"},
{"application/x-sv4crc","sv4crc"},
{"application/x-tar","tar"},
{"application/x-ustar","ustar"},
{"application/x-winhelp","hlp"},
{"audio/midi","mid"},
{"audio/midi","midi"},
{"audio/midi","kar"},
{"audio/mpeg","mp3"},
{"audio/mpeg","mpga"},
{"audio/mpeg","mp2"},
{"audio/basic","au"},
{"audio/basic","snd"},
{"audio/x-aiff","aif"},
{"audio/x-aiff","aiff"},
{"audio/x-aiff","aifc"},
{"audio/x-pn-realaudio","rm"},
{"audio/x-pn-realaudio","ram"},
{"audio/x-pn-realaudio","ra"},
{"audio/x-pn-realaudio-plugin","rpm"},
{"audio/x-wav","wav"},
{"chemical/x-pdb","pdb"},
{"chemical/x-pdb","xyz"},
{"drawing/x-dwf","dwf"},
{"image/gif","gif"},
{"image/ief","ief"},
{"image/jpeg","jpg"},
{"image/jpeg","jpe"},
{"image/jpeg","jpeg"},
{"image/pict","pict"},
{"image/png","png"},
{"image/tiff","tiff"},
{"image/tiff","tif"},
{"image/svg+xml","svg"},
{"image/x-cmu-raster","ras"},
{"image/x-freehand","fh4"},
{"image/x-freehand","fh7"},
{"image/x-freehand","fh5"},
{"image/x-freehand","fhc"},
{"image/x-freehand","fh"},
{"image/x-portable-anymap","pnm"},
{"image/x-portable-bitmap","pgm"},
{"image/x-portable-pixmap","ppm"},
{"image/x-rgb","rgb"},
{"image/x-xbitmap","xbm"},
{"image/x-xpixmap","xpm"},
{"image/x-xwindowdump","xwd"},
{"model/mesh","msh"},
{"model/mesh","mesh"},
{"model/mesh","silo"},
{"multipart/x-zip","zip"},
{"multipart/x-gzip","gzip"},
{"text/css","css"},
{"text/html","html"},
{"text/html","htm"},
{"text/plain","txt"},
{"text/plain","g"},
{"text/plain","h"},
{"text/plain","c"},
{"text/plain","cc"},
{"text/plain","hh"},
{"text/plain","m"},
{"text/plain","f90"},
{"text/richtext","rtx"},
{"text/tab-separated-values","tsv"},
{"text/x-setext","etx"},
{"text/x-sgml","sgml"},
{"text/x-sgml","sgm"},
{"text/xml","xml"},
{"text/xml","dtd"},
{"video/mpeg","mpeg"},
{"video/mpeg","mpg"},
{"video/mpeg","mpe"},
{"video/quicktime","qt"},
{"video/quicktime","mov"},
{"video/x-msvideo","avi"},
{"video/x-sgi-movie","movie"},
{"x-conference/x-cooltalk","ice"},
/*{"application/x-httpd-cgi","cgi"},*/
{"x-world/x-vrml","wrl"},
{"*","class"},
{"",""}};
// Reserved (RFC2396)
#define CHAR_RESERVED(c) ( strchr(";/?:@&=+$,",(unsigned char)(c)) != 0 )
// Delimiters (RFC2396)
#define CHAR_DELIM(c) ( strchr("<>#%\"",(unsigned char)(c)) != 0 )
// Unwise (RFC2396)
#define CHAR_UNWISE(c) ( strchr("{}|\\^[]`",(unsigned char)(c)) != 0 )
// Special (escape chars) (RFC2396 + >127 )
#define CHAR_LOW(c) ( ((unsigned char)(c) <= 31) )
#define CHAR_HIG(c) ( ((unsigned char)(c) >= 127) )
#define CHAR_SPECIAL(c) ( CHAR_LOW(c) || CHAR_HIG(c) )
// We try to avoid them and encode them instead
#define CHAR_XXAVOID(c) ( strchr(" *'\"!",(unsigned char)(c)) != 0 )
// conversion Θventuelle / vers antislash
#if HTS_WIN
char* antislash(char* s) {
char* buff;
char* a;
NOSTATIC_RESERVE(buff, char, HTS_URLMAXSIZE*2);
strcpy(buff,s);
while(a=strchr(buff,'/')) *a='\\';
return buff;
}
#endif
// RΘcupΘration d'un fichier http sur le net.
// Renvoie une adresse sur le bloc de mΘmoire, ou bien
// NULL si un retour.msgeur (buffer retour.msg) est survenue.
//
// Une adresse de structure htsmsg peut Ωtre transmise pour
// suivre l'Θvolution du chargement si le process a ΘtΘ lancΘ
// en background
htsblk httpget(char* url) {
char adr[HTS_URLMAXSIZE*2]; // adresse
char fil[HTS_URLMAXSIZE*2]; // chemin
// sΘparer URL en adresse+chemin
if (ident_url_absolute(url,adr,fil)==-1) {
htsblk retour;
memset(&retour, 0, sizeof(htsblk)); // effacer
// retour prΘdΘfini: erreur
retour.adr=NULL;
retour.size=0;
retour.msg[0]='\0';
retour.statuscode=-1;
strcpy(retour.msg,"Error invalid URL");
return retour;
}
return xhttpget(adr,fil);
}
// ouvre une liaison http, envoie une requΦte GET et rΘceptionne le header
// retour: socket
int http_fopen(char* adr,char* fil,htsblk* retour) {
// / GET, traiter en-tΩte
return http_xfopen(0,1,1,NULL,adr,fil,retour);
}
// ouverture d'une liaison http, envoi d'une requΦte
// mode: 0 GET 1 HEAD [2 POST]
// treat: traiter header?
// waitconnect: attendre le connect()
// note: dans retour, on met les params du proxy
int http_xfopen(int mode,int treat,int waitconnect,char* xsend,char* adr,char* fil,htsblk* retour) {
//htsblk retour;
//int bufl=TAILLE_BUFFER; // 8Ko de buffer
T_SOC soc=INVALID_SOCKET;
//char *p,*q;
// retour prΘdΘfini: erreur
if (retour) {
retour->adr=NULL;
retour->size=0;
retour->msg[0]='\0';
retour->statuscode=-5; // a priori erreur non fatale
}
#if HDEBUG
printf("adr=%s\nfichier=%s\n",adr,fil);
#endif
// ouvrir liaison
#if HDEBUG
printf("CrΘation d'une socket sur %s\n",adr);
#endif
#if CNXDEBUG
printf("..newhttp\n");
#endif
/* connexion */
if (retour) {
if ( (!(retour->req.proxy.active)) || (strcmp(adr,"file://")==0) ) { /* pas de proxy, ou non utilisable ici */
soc=newhttp(adr,retour,-1,waitconnect);
} else {
soc=newhttp(retour->req.proxy.name,retour,retour->req.proxy.port,waitconnect); // ouvrir sur le proxy α la place
}
} else {
soc=newhttp(adr,NULL,-1,waitconnect);
}
// copier index socket retour
if (retour) retour->soc=soc;
/* Check for errors */
if (soc == INVALID_SOCKET) {
if (retour) {
if (retour->msg) {
if (!strnotempty(retour->msg)) {
strcpy(retour->msg,"Connect error");
}
}
}
}
// --------------------
// court-circuit (court circuite aussi le proxy..)
// LOCAL_SOCKET_ID est une pseudo-socket locale
if (soc==LOCAL_SOCKET_ID) {
retour->is_file=1; // fichier local
if (mode==0) { // GET
// Test en cas de file:///C|...
if (!fexist(fconv(unescape_http(fil))))
if (fexist(fconv(unescape_http(fil+1)))) {
char tempo[HTS_URLMAXSIZE*2];
strcpy(tempo,fil+1);
strcpy(fil,tempo);
}
// Ouvrir
retour->totalsize=fsize(fconv(unescape_http(fil))); // taille du fichier
retour->msg[0]='\0';
soc=INVALID_SOCKET;
if (retour->totalsize<0)
strcpy(retour->msg,"Unable to open file");
else if (retour->totalsize==0)
strcpy(retour->msg,"File empty");
else {
// Note: On passe par un FILE* (plus propre)
//soc=open(fil,O_RDONLY,0); // en lecture seule!
retour->fp=fopen(fconv(unescape_http(fil)),"rb"); // ouvrir
if (retour->fp==NULL)
soc=INVALID_SOCKET;
else
soc=LOCAL_SOCKET_ID;
}
retour->soc=soc;
if (soc!=INVALID_SOCKET) {
retour->statuscode=200; // OK
strcpy(retour->msg,"OK");
guess_httptype(retour->contenttype,fil);
} else if (strnotempty(retour->msg)==0)
strcpy(retour->msg,"Unable to open file");
return soc; // renvoyer
} else { // HEAD ou POST : interdit sur un local!!!! (c'est idiot!)
strcpy(retour->msg,"Unexpected Head/Post local request");
soc=INVALID_SOCKET; // erreur
retour->soc=soc;
return soc;
}
}
// --------------------
if (soc!=INVALID_SOCKET) {
char rcvd[1100];
rcvd[0]='\0';
#if HDEBUG
printf("Ok, connexion rΘussie, id=%d\n",soc);
#endif
// connectΘ?
if (waitconnect) {
http_sendhead(NULL,mode,xsend,adr,fil,NULL,NULL,retour);
}
if (soc!=INVALID_SOCKET) {
#if HDEBUG
printf("Attente de la rΘponse:\n");
#endif
// si GET (rΘception d'un fichier), rΘceptionner en-tΩte d'abord,
// et ensuite le corps
// si POST on ne rΘceptionne rien du tout, c'est aprΦs que l'on fera
// une rΘception standard pour rΘcupΘrer l'en tΩte
if ((treat) && (waitconnect)) { // traiter (attendre!) en-tΩte
// RΘception de la status line et de l'en-tΩte (norme RFC1945)
// status-line α rΘcupΘrer
finput(soc,rcvd,1024);
if (strnotempty(rcvd)==0)
finput(soc,rcvd,1024); // "certains serveurs buggΘs envoient un \n au dΘbut" (RFC)
// traiter status-line
treatfirstline(retour,rcvd);
#if HDEBUG
printf("Status-Code=%d\n",retour->statuscode);
#endif
// en-tΩte
// header // ** !attention! HTTP/0.9 non supportΘ
do {
finput(soc,rcvd,1024);
#if HDEBUG
printf(">%s\n",rcvd);
#endif
if (strnotempty(rcvd))
treathead(NULL,NULL,NULL,retour,rcvd); // traiter
} while(strnotempty(rcvd));
//rcvsize=-1; // forCER CHARGEMENT INCONNU
//if (retour)
// retour->totalsize=rcvsize;
} else { // si GET, on recevra l'en tΩte APRES
//rcvsize=-1; // on ne connait pas la taille de l'en-tΩte
if (retour)
retour->totalsize=-1;
}
}
}
return soc;
}
// envoi d'une requΦte
int http_sendhead(t_cookie* cookie,int mode,char* xsend,char* adr,char* fil,char* referer_adr,char* referer_fil,htsblk* retour) {
char buff[8192];
//int use_11=0; // HTTP 1.1 utilisΘ
int direct_url=0; // ne pas analyser l'url (exemple: ftp://)
char* search_tag=NULL;
buff[0]='\0';
// header Date
//strcat(buff,"Date: ");
//time_gmt_rfc822(buff); // obtenir l'heure au format rfc822
//sendc("\n");
//strcat(buff,buff);
// possibilitΘ non documentΘe: >post: et >postfile:
// si prΘsence d'un tag >post: alors executer un POST
// exemple: http://www.someweb.com/test.cgi?foo>post:posteddata=10&foo=5
// si prΘsence d'un tag >postfile: alors envoyer en tΩte brut contenu dans le fichier en question
// exemple: http://www.someweb.com/test.cgi?foo>postfile:post0.txt
search_tag=strstr(fil,POSTTOK":");
if (!search_tag) {
search_tag=strstr(fil,POSTTOK"file:");
if (search_tag) { // postfile
if (mode==0) { // GET!
FILE* fp=fopen(unescape_http(search_tag+strlen(POSTTOK)+5),"rb");
if (fp) {
char line[1100];
char protocol[256],url[HTS_URLMAXSIZE*2],method[256];
linput(fp,line,1000);
if (sscanf(line,"%s %s %s",method,url,protocol) == 3) {
// selon que l'on a ou pas un proxy
if (retour->req.proxy.active)
sprintf(buff,"%s http://%s%s %s\r\n",method,adr,url,protocol);
else
sprintf(buff,"%s %s %s\r\n",method,url,protocol);
// lire le reste en brut
fread(buff+strlen(buff),8000-strlen(buff),1,fp);
}
fclose(fp);
}
}
}
}
// Fin postfile
if (strnotempty(buff)==0) { // PAS POSTFILE
// Type de requΦte?
if ((search_tag) && (mode==0)) {
strcat(buff,"POST ");
} else if (mode==0) { // GET
strcat(buff,"GET ");
} else { // if (mode==1) {
if (!retour->req.http11) // forcer HTTP/1.0
strcat(buff,"GET "); // certains serveurs (cgi) buggent avec HEAD
else
strcat(buff,"HEAD ");
}
// si on gΦre un proxy, il faut une Absolute URI: on ajoute avant http://www.adr.dom
if (retour->req.proxy.active) {
if (!strfield(adr,"ftp://")) {
#if HDEBUG
printf("Proxy Use: for %s%s proxy %d port %d\n",adr,fil,retour->req.proxy.name,retour->req.proxy.port);
#endif
strcat(buff,"http://");
strcat(buff,jump_identification(adr));
} else { // ftp:// en proxy http
#if HDEBUG
printf("Proxy Use for ftp: for %s%s proxy %d port %d\n",adr,fil,retour->req.proxy.name,retour->req.proxy.port);
#endif
direct_url=1; // ne pas analyser user/pass
strcat(buff,adr);
}
}
// NOM DU FICHIER
// on slash doit Ωtre prΘsent en dΘbut, sinon attention aux bad request! (400)
if (*fil!='/') strcat(buff,"/");
{
char tempo[HTS_URLMAXSIZE*2];
tempo[0]='\0';
if (search_tag)
strncat(tempo,fil,(int) (search_tag - fil));
else
strcpy(tempo,fil);
escape_check_url(tempo);
strcat(buff,tempo); // avec Θchappement
}
// protocole
if (!retour->req.http11) { // forcer HTTP/1.0
//use_11=0;
strcat(buff," HTTP/1.0\x0d\x0a");
} else { // RequΦte 1.1
//use_11=1;
strcat(buff," HTTP/1.1\x0d\x0a");
}
/* supplemental data */
if (xsend) strcat(buff,xsend); // Θventuelles autres lignes
// tester proxy authentication
if (retour->req.proxy.active) {
char* a=jump_identification(retour->req.proxy.name);
if (a!=retour->req.proxy.name) { // et hop, authentification proxy!
char autorisation[1100];
char user_pass[256];
autorisation[0]=user_pass[0]='\0';
//
strncat(user_pass,retour->req.proxy.name,(int) (a - retour->req.proxy.name) - 1);
strcpy(user_pass,unescape_http(user_pass));
code64(user_pass,autorisation);
strcat(buff,"Proxy-Authorization: Basic ");
strcat(buff,autorisation);
strcat(buff,H_CRLF);
#if HDEBUG
printf("Proxy-Authenticate, %s (code: %s)\n",user_pass,autorisation);
#endif
}
}
// Referer?
if ((referer_adr) && (referer_fil)) { // existe
if ((strnotempty(referer_adr)) && (strnotempty(referer_fil))) { // non vide
if (strcmp(referer_adr,"file://")) { // PAS file://
strcat(buff,"Referer: ");
strcat(buff,"http://");
strcat(buff,jump_identification(referer_adr));
strcat(buff,referer_fil);
strcat(buff,H_CRLF);
}
}
}
// POST?
if (mode==0) { // GET!
if (search_tag) {
char clen[256];
sprintf(clen,"Content-length: %d"H_CRLF,(int)(strlen(unescape_http(search_tag+strlen(POSTTOK)+1))));
strcat(buff,clen);
}
}
// gestion cookies?
if (cookie) {
char* b=cookie->data;
int cook=0;
int max_cookies=8;
int max_size=2048;
max_size+=strlen(buff);
do {
b=cookie_find(b,"",jump_identification(adr),fil); // prochain cookie satisfaisant aux conditions
if (b) {
max_cookies--;
if (!cook) {
strcat(buff,"Cookie: ");
strcat(buff,"$Version=1; ");
cook=1;
} else
strcat(buff,"; ");
strcat(buff,cookie_get(b,5));
strcat(buff,"=");
strcat(buff,cookie_get(b,6));
strcat(buff,"; $Path=");
strcat(buff,cookie_get(b,2));
b=cookie_nextfield(b);
}
} while( (b) && (max_cookies>0) && ((int)strlen(buff)<max_size));
if (cook) { // on a envoyΘ un (ou plusieurs) cookie?
strcat(buff,H_CRLF);
#if DEBUG_COOK
printf("Header:\n%s\n",buff);
#endif
}
}
// connection close?
//if (use_11) // Si on envoie une requΦte 1.1, prΘciser qu'on ne veut pas de keep-alive!!
strcat(buff,"Connection: close"H_CRLF);
// gΘrer le keep-alive (garder socket)
//strcat(buff,"Connection: Keep-Alive\n");
{
char* real_adr=jump_identification(adr);
//if ((use_11) || (retour->user_agent_send)) { // Pour le 1.1 on utilise un Host:
if (!direct_url) { // pas ftp:// par exemple
//if (!retour->req.proxy.active) {
strcat(buff,"Host: "); strcat(buff,real_adr); strcat(buff,H_CRLF);
//}
}
//}
// PrΘsence d'un user-agent?
if (retour->req.user_agent_send) { // ohh un user-agent
char s[256];
// HyperTextSeeker/"HTSVERSION
sprintf(s,"User-Agent: %s"H_CRLF,retour->req.user_agent);
strcat(buff,s);
// pour les serveurs difficiles
strcat(buff,"Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/svg+xml, */*"H_CRLF);
if (strnotempty(retour->req.lang_iso)) {
strcat(buff,"Accept-Language: "); strcat(buff,retour->req.lang_iso); strcat(buff,H_CRLF);
}
strcat(buff,"Accept-Charset: iso-8859-1, *"H_CRLF);
if (retour->req.http11) {
#if HTS_USEZLIB
if ((!retour->req.range_used) && (!retour->req.nocompression))
strcat(buff,"Accept-Encoding: gzip, deflate, compress, identity"H_CRLF);
else
strcat(buff,"Accept-Encoding: identity"H_CRLF); /* no compression */
#else
strcat(buff,"Accept-Encoding: identity"H_CRLF); /* no compression */
#endif
}
} else {
strcat(buff,"Accept: */*"H_CRLF); // le minimum
}
/* Authentification */
{
char autorisation[1100];
char* a;
autorisation[0]='\0';
if (real_adr != adr) { // ohh une authentification!
if (!direct_url) { // pas ftp:// par exemple
char user_pass[256];
user_pass[0]='\0';
strncat(user_pass,adr,(int) (real_adr - adr) - 1);
strcpy(user_pass,unescape_http(user_pass));
code64(user_pass,autorisation);
if (strcmp(fil,"/robots.txt")) /* pas robots.txt */
bauth_add(cookie,real_adr,fil,autorisation);
}
} else if ( (a=bauth_check(cookie,real_adr,fil)) )
strcpy(autorisation,a);
/* On a une autorisation a donner? */
if (strnotempty(autorisation)) {
strcat(buff,"Authorization: Basic ");
strcat(buff,autorisation);
strcat(buff,H_CRLF);
}
}
}
//strcat(buff,"Accept-Language: en\n");
//strcat(buff,"Accept-Charset: iso-8859-1,*,utf-8\n");
// CRLF de fin d'en tΩte
strcat(buff,H_CRLF);
// donnΘes complΘmentaires?
if (search_tag)
if (mode==0) // GET!
strcat(buff,unescape_http(search_tag+strlen(POSTTOK)+1));
}
#if HDEBUG
#endif
if (_DEBUG_HEAD) {
if (ioinfo) {
fprintf(ioinfo,"request for %s%s:\r\n",jump_identification(adr),fil);
fprintfio(ioinfo,buff,"<<< ");
fprintf(ioinfo,"\r\n");
fflush(ioinfo);
}
} // Fin test pas postfile
//
// Envoi
if (sendc(retour, buff)<0) { // ERREUR, socket rompue?...
//if (sendc(retour->soc,buff) != strlen(buff)) { // ERREUR, socket rompue?...
deletesoc_r(retour); // fermer tout de mΩme
// et tenter de reconnecter
strcpy(retour->msg,"Broken pipe");
retour->soc=INVALID_SOCKET;
}
// RX'98
return 0;
}
// traiter 1ere ligne d'en tΩte
void treatfirstline(htsblk* retour,char* rcvd) {
char* a=rcvd;
// exemple:
// HTTP/1.0 200 OK
if (*a) {
// note: certains serveurs buggΘs renvoient HTTP/1.0\n200 OK ou " HTTP/1.0 200 OK"
while ((*a==' ') || (*a==10) || (*a==13) || (*a==9)) a++; // Θpurer espaces au dΘbut
if (strfield(a, "HTTP/")) {
// sauter HTTP/1.x
while ((*a!=' ') && (*a!='\0') && (*a!=10) && (*a!=13) && (*a!=9)) a++;
if (*a != '\0') {
while ((*a==' ') || (*a==10) || (*a==13) || (*a==9)) a++; // Θpurer espaces
if ((*a>='0') && (*a<='9')) {
sscanf(a,"%d",&(retour->statuscode));
// sauter 200
while ((*a!=' ') && (*a!='\0') && (*a!=10) && (*a!=13) && (*a!=9)) a++;
while ((*a==' ') || (*a==10) || (*a==13) || (*a==9)) a++; // Θpurer espaces
if ((strlen(a) > 1) && (strlen(a) < 64) ) // message retour
strcpy(retour->msg,a);
else
infostatuscode(retour->msg,retour->statuscode);
// type MIME par dΘfaut
strcpy(retour->contenttype,HTS_HYPERTEXT_DEFAULT_MIME);
} else { // pas de code!
retour->statuscode=-1;
strcpy(retour->msg,"Unknown response structure");
}
} else { // euhh??
retour->statuscode=-1;
strcpy(retour->msg,"Unknown response structure");
}
} else {
if (strnotempty(a)) {
retour->statuscode=-1;
strcpy(retour->msg,"Unknown response structure, no HTTP/ response given");
} else {
/* This is dirty .. */
retour->statuscode=200;
strcpy(retour->msg, "Unknown, assuming junky server");
strcpy(retour->contenttype,HTS_HYPERTEXT_DEFAULT_MIME);
}
}
} else { // vide!
/*
retour->statuscode=-1;
strcpy(retour->msg,"Empty reponse or internal error");
*/
/* This is dirty .. */
retour->statuscode=200;
strcpy(retour->msg, "Unknown, assuming junky server");
strcpy(retour->contenttype,HTS_HYPERTEXT_DEFAULT_MIME);
}
}
// traiter ligne par ligne l'en tΩte
// gestion des cookies
void treathead(t_cookie* cookie,char* adr,char* fil,htsblk* retour,char* rcvd) {
int p;
if ((p=strfield(rcvd,"Content-length:"))!=0) {
#if HDEBUG
printf("ok, Content-length: dΘtectΘ\n");
#endif
sscanf(rcvd+p,LLintP,&(retour->totalsize));
}
else if ((p=strfield(rcvd,"Content-Disposition:"))!=0) {
while(*(rcvd+p)==' ') p++; // sauter espaces
if ((int) strlen(rcvd+p)<250) { // pas trop long?
char tmp[256];
char *a=NULL,*b=NULL;
strcpy(tmp,rcvd+p);
a=strstr(tmp,"filename=");
if (a) {
a+=strlen("filename=");
while(is_space(*a)) a++;
//a=strchr(a,'"');
if (a) {
char *c=NULL;
//a++; /* jump " */
while((c=strchr(a,'/'))) /* skip all / (see RFC2616) */
a=c+1;
//b=strchr(a+1,'"');
b=a+strlen(a)-1;
while(is_space(*b)) b--;
b++;
if (b) {
*b='\0';
if ((int) strlen(a) < 200) { // pas trop long?
strcpy(retour->cdispo,a);
}
}
}
}
}
}
else if ((p=strfield(rcvd,"Last-Modified:"))!=0) {
while(*(rcvd+p)==' ') p++; // sauter espaces
if ((int) strlen(rcvd+p)<64) { // pas trop long?
//struct tm* tm_time=convert_time_rfc822(rcvd+p);
strcpy(retour->lastmodified,rcvd+p);
}
}
else if ((p=strfield(rcvd,"Date:"))!=0) {
if (strnotempty(retour->lastmodified)==0) { /* pas encore de last-modified */
while(*(rcvd+p)==' ') p++; // sauter espaces
if ((int) strlen(rcvd+p)<64) { // pas trop long?
//struct tm* tm_time=convert_time_rfc822(rcvd+p);
strcpy(retour->lastmodified,rcvd+p);
}
}
}
else if ((p=strfield(rcvd,"Etag:"))!=0) { /* Etag */
if (retour) {
while(*(rcvd+p)==' ') p++; // sauter espaces
if ((int) strlen(rcvd+p)<64) // pas trop long?
strcpy(retour->etag,rcvd+p);
else // erreur.. ignorer
retour->etag[0]='\0';
}
}
else if ((p=strfield(rcvd,"Transfer-Encoding: chunked"))!=0) { // chunk!
retour->is_chunk=1; // chunked
//retour->http11=2; // chunked
#if HDEBUG
printf("ok, Transfer-Encoding: dΘtectΘ\n");
#endif
}
else if ((p=strfield(rcvd,"Content-type:"))!=0) {
if (retour) {
char tempo[1100];
// Θviter les text/html; charset=foo
{
char* a=strchr(rcvd+p,';');
if (a) *a='\0';
}
sscanf(rcvd+p,"%s",tempo);
if (strlen(tempo)<64) // pas trop long!!
strcpy(retour->contenttype,tempo);
else
strcpy(retour->contenttype,"application/octet-stream-unknown"); // erreur
}
}
else if ((p=strfield(rcvd,"Content-Range:"))!=0) {
char* a=strstr(rcvd+p,"*/");
if (a) {
if (sscanf(a+2,LLintP,&retour->crange) != 1) {
retour->crange=0;
}
}
}
else if ((p=strfield(rcvd,"Content-Encoding:"))!=0) {
if (retour) {
char tempo[1100];
{
char* a=strchr(rcvd+p,';');
if (a) *a='\0';
}
sscanf(rcvd+p,"%s",tempo);
if (strlen(tempo)<64) // pas trop long!!
strcpy(retour->contentencoding,tempo);
else
retour->contentencoding[0]='\0'; // erreur
#if HTS_USEZLIB
if (retour->contentencoding[0])
retour->compressed=1;
#endif
}
}
else if ((p=strfield(rcvd,"Location:"))!=0) {
if (retour) {
if (retour->location) {
while(*(rcvd+p)==' ') p++; // sauter espaces
if ((int) strlen(rcvd+p)<HTS_URLMAXSIZE) // pas trop long?
strcpy(retour->location,rcvd+p);
else // erreur.. ignorer
retour->location[0]='\0';
}
}
}
else if ((p=strfield(rcvd,"Connection: Keep-Alive"))!=0) {
// non, pas de keep-alive! on dΘconnectera..
}
else if ((p=strfield(rcvd,"Keep-Alive:"))!=0) { // params keep-alive
// rien α faire
}
else if ( ((p=strfield(rcvd,"Set-Cookie:"))!=0) && (cookie) ) { // ohh un cookie
char* a = rcvd+p; // pointeur
char domain[256]; // domaine cookie (.netscape.com)
char path[256]; // chemin (/)
char cook_name[256]; // nom cookie (MYCOOK)
char cook_value[8192]; // valeur (ID=toto,S=1234)
#if DEBUG_COOK
printf("set-cookie detected\n");
#endif
while(*a) {
char *token_st,*token_end;
char *value_st,*value_end;
char name[256];
char value[8192];
int next=0;
name[0]=value[0]='\0';
//
// initialiser cookie lu actuellement
if (adr)
strcpy(domain,jump_identification(adr)); // domaine
strcpy(path,"/"); // chemin (/)
strcpy(cook_name,""); // nom cookie (MYCOOK)
strcpy(cook_value,""); // valeur (ID=toto,S=1234)
// boucler jusqu'au prochain cookie ou la fin
do {
char* start_loop=a;
while(is_space(*a)) a++; // sauter espaces
token_st=a; // dΘpart token
while((!is_space(*a)) && (*a) && (*a!=';') && (*a!='=')) a++; // arrΩter si espace, point virgule
token_end=a;
while(is_space(*a)) a++; // sauter espaces
if (*a=='=') { // name=value
a++;
while(is_space(*a)) a++; // sauter espaces
value_st=a;
while( (*a!=';') && (*a)) a++; // prochain ;
//while( ((*a!='"') || (*(a-1)=='\\')) && (*a)) a++; // prochain " (et pas \")
value_end=a;
//if (*a==';') { // finit par un ;
// vΘrifier dΘbordements
if ( (((int) (token_end - token_st))<200) && (((int) (value_end - value_st))<8000)
&& (((int) (token_end - token_st))>0) && (((int) (value_end - value_st))>0) ) {
name[0]='\0';
value[0]='\0';
strncat(name,token_st,(int) (token_end - token_st));
strncat(value,value_st,(int) (value_end - value_st));
#if DEBUG_COOK
printf("detected cookie-av: name=\"%s\" value=\"%s\"\n",name,value);
#endif
if (strfield2(name,"domain")) {
strcpy(domain,value);
}
else if (strfield2(name,"path")) {
strcpy(path,value);
}
else if (strfield2(name,"max-age")) {
// ignorΘ..
}
else if (strfield2(name,"expires")) {
// ignorΘ..
}
else if (strfield2(name,"version")) {
// ignorΘ..
}
else if (strfield2(name,"comment")) {
// ignorΘ
}
else if (strfield2(name,"secure")) { // ne devrait pas arriver ici
// ignorΘ
}
else {
if (strnotempty(cook_name)==0) { // noter premier: nom et valeur cookie
strcpy(cook_name,name);
strcpy(cook_value,value);
} else { // prochain cookie
a=start_loop; // on devra recommencer α cette position
next=1; // enregistrer
}
}
}
}
if (!next) {
while((*a!=';') && (*a)) a++; // prochain
while(*a==';') a++; // sauter ;
}
} while((*a) && (!next));
if (strnotempty(cook_name)) { // cookie?
#if DEBUG_COOK
printf("new cookie: name=\"%s\" value=\"%s\" domain=\"%s\" path=\"%s\"\n",cook_name,cook_value,domain,path);
#endif
cookie_add(cookie,cook_name,cook_value,domain,path);
}
}
}
}
// transforme le message statuscode en chaεne
void infostatuscode(char* msg,int statuscode) {
switch( statuscode) {
// Erreurs HTTP, selon RFC
case 100: strcpy( msg,"Continue"); break;
case 101: strcpy( msg,"Switching Protocols"); break;
case 200: strcpy( msg,"OK"); break;
case 201: strcpy( msg,"Created"); break;
case 202: strcpy( msg,"Accepted"); break;
case 203: strcpy( msg,"Non-Authoritative Information"); break;
case 204: strcpy( msg,"No Content"); break;
case 205: strcpy( msg,"Reset Content"); break;
case 206: strcpy( msg,"Partial Content"); break;
case 300: strcpy( msg,"Multiple Choices"); break;
case 301: strcpy( msg,"Moved Permanently"); break;
case 302: strcpy( msg,"Moved Temporarily"); break;
case 303: strcpy( msg,"See Other"); break;
case 304: strcpy( msg,"Not Modified"); break;
case 305: strcpy( msg,"Use Proxy"); break;
case 306: strcpy( msg,"Undefined 306 error"); break;
case 307: strcpy( msg,"Temporary Redirect"); break;
case 400: strcpy( msg,"Bad Request"); break;
case 401: strcpy( msg,"Unauthorized"); break;
case 402: strcpy( msg,"Payment Required"); break;
case 403: strcpy( msg,"Forbidden"); break;
case 404: strcpy( msg,"Not Found"); break;
case 405: strcpy( msg,"Method Not Allowed"); break;
case 406: strcpy( msg,"Not Acceptable"); break;
case 407: strcpy( msg,"Proxy Authentication Required"); break;
case 408: strcpy( msg,"Request Time-out"); break;
case 409: strcpy( msg,"Conflict"); break;
case 410: strcpy( msg,"Gone"); break;
case 411: strcpy( msg,"Length Required"); break;
case 412: strcpy( msg,"Precondition Failed"); break;
case 413: strcpy( msg,"Request Entity Too Large"); break;
case 414: strcpy( msg,"Request-URI Too Large"); break;
case 415: strcpy( msg,"Unsupported Media Type"); break;
case 416: strcpy( msg,"Requested Range Not Satisfiable"); break;
case 417: strcpy( msg,"Expectation Failed"); break;
case 500: strcpy( msg,"Internal Server Error"); break;
case 501: strcpy( msg,"Not Implemented"); break;
case 502: strcpy( msg,"Bad Gateway"); break;
case 503: strcpy( msg,"Service Unavailable"); break;
case 504: strcpy( msg,"Gateway Time-out"); break;
case 505: strcpy( msg,"HTTP Version Not Supported"); break;
//
default: if (strnotempty(msg)==0) strcpy( msg,"Unknown error"); break;
}
}
// identique au prΘcΘdent, sauf que l'on donne adr+fil et non url complΦte
htsblk xhttpget(char* adr,char* fil) {
T_SOC soc;
htsblk retour;
memset(&retour, 0, sizeof(htsblk));
soc=http_fopen(adr,fil,&retour);
if (soc!=INVALID_SOCKET) {
http_fread(soc,&retour);
#if HTS_DEBUG_CLOSESOCK
DEBUG_W("xhttpget: deletehttp\n");
#endif
if (retour.soc!=INVALID_SOCKET) deletehttp(&retour); // fermer
retour.soc=INVALID_SOCKET;
}
return retour;
}
// variation sur un thΦme...
// rΘceptionne uniquement un en-tΩte (HEAD)
// retourne dans xx.adr l'adresse pointant sur le bloc de mΘmoire de l'en tΩte
htsblk http_gethead(char* adr,char* fil) {
T_SOC soc;
htsblk retour;
memset(&retour, 0, sizeof(htsblk));
soc=http_xfopen(1,0,1,NULL,adr,fil,&retour); // HEAD, pas de traitement en-tΩte
if (soc!=INVALID_SOCKET) {
http_fread(soc,&retour); // rΘception en-tΩte
#if HTS_DEBUG_CLOSESOCK
DEBUG_W("http_gethead: deletehttp\n");
#endif
if (retour.soc!=INVALID_SOCKET) deletehttp(&retour); // fermer
retour.soc=INVALID_SOCKET;
}
return retour;
}
// oui ca ressemble vachement α xhttpget - en Θtant sobre on peut voir LA diffΘrence..
// lecture sur une socket ouverte, le header a dΘja ΘtΘ envoyΘ dans le cas de GET
// il ne reste plus qu'α lire les donnΘes
// (pour HEAD le header est lu ici!)
void http_fread(T_SOC soc,htsblk* retour) {
//int bufl=TAILLE_BUFFER; // 8Ko de buffer
if (retour) retour->soc=soc;
if (soc!=INVALID_SOCKET) {
// fonction de lecture d'une socket (plus propre)
while(http_fread1(retour)!=-1);
soc=retour->soc;
if (retour->adr==NULL) {
if (strnotempty(retour->msg)==0)
sprintf(retour->msg,"Unable to read");
return ; // erreur
}
#if HDEBUG
printf("Ok, donnΘes reτues\n");
#endif
return ;
}
return ;
}
// check if data is available
int check_readinput(htsblk* r) {
if (r->soc != INVALID_SOCKET) {
fd_set fds; // poll structures
struct timeval tv; // structure for select
FD_ZERO(&fds);
FD_SET(r->soc,&fds);
tv.tv_sec=0;
tv.tv_usec=0;
select(r->soc + 1,&fds,NULL,NULL,&tv);
if (FD_ISSET(r->soc,&fds))
return 1;
else
return 0;
} else
return 0;
}
// lecture d'un bloc sur une socket (ou un fichier!)
// >=0 : nombre d'octets lus
// <0 : fin ou erreur
HTS_INLINE LLint http_fread1(htsblk* r) {
//int bufl=TAILLE_BUFFER; // taille d'un buffer max.
return http_xfread1(r,TAILLE_BUFFER);
}
// idem, sauf qu'ici on peut choisir la taille max de donnΘes α recevoir
// SI bufl==0 alors le buffer est censΘ Ωtre de 8kos, et on recoit par bloc de lignes
// en Θliminant les cr (ex: header), arrΩt si double-lf
// SI bufl==-1 alors le buffer est censΘ Ωtre de 8kos, et on recoit ligne par ligne
// en Θliminant les cr (ex: header), arrΩt si double-lf
// Note: les +1 dans les malloc sont d√s α l'octet nul rajoutΘ en fin de fichier
LLint http_xfread1(htsblk* r,int bufl) {
int nl=-1;
if (bufl>0) {
if (!r->is_write) { // stocker en mΘmoire
if (r->totalsize>0) { // totalsize dΘterminΘ ET ALLOUE
if (r->adr==NULL) {
r->adr=(char*) malloct((INTsys) r->totalsize + 1);
r->size=0;
}
if (r->adr!=NULL) {
// lecture
nl = hts_read(r,r->adr + ((int) r->size),(int) (r->totalsize-r->size) ); /* NO 32 bit overlow possible here (no 4GB html!) */
// nouvelle taille
if (nl >= 0) r->size+=nl;
if ((nl < 0) || (r->size >= r->totalsize))
nl=-1; // break
r->adr[r->size]='\0'; // caractΦre NULL en fin au cas o∙ l'on traite des HTML
}
} else { // inconnu..
// rΘserver de la mΘmoire?
if (r->adr==NULL) {
#if HDEBUG
printf("..alloc xfread\n");
#endif
r->adr=(char*) malloct(bufl + 1);
r->size=0;
}
else {
#if HDEBUG
printf("..realloc xfread1\n");
#endif
r->adr=(char*) realloct(r->adr,(int)r->size+bufl + 1);
}
if (r->adr!=NULL) {
// lecture
nl = hts_read(r,r->adr+(int)r->size,bufl);
if (nl>0) {
// resize
r->adr=(char*) realloct(r->adr,(int)r->size+nl + 1);
// nouvelle taille
r->size+=nl;
// octet nul
if (r->adr) r->adr[r->size]='\0';
} // sinon on a fini
#if HDEBUG
else if (nl < 0)
printf("..end read (%d)\n", nl);
#endif
}
#if HDEBUG
else printf("..-> error\n");
#endif
}
// pas de adr=erreur
if (r->adr==NULL) nl=-1;
} else { // stocker sur disque
char* buff;
buff=(char*) malloct(bufl);
if (buff!=NULL) {
// lecture
nl = hts_read(r,buff,bufl);
// nouvelle taille
if (nl > 0) {
r->size+=nl;
if ((int) fwrite(buff,1,nl,r->out)!=nl) {
r->statuscode=-1;
strcpy(r->msg,"Write error on disk");
nl=-1;
}
}
if ((nl < 0) || ((r->totalsize>0) && (r->size >= r->totalsize)))
nl=-1; // break
// libΘrer bloc tempo
freet(buff);
} else
nl=-1;
if ((nl < 0) && (r->out!=NULL)) {
fflush(r->out);
}
} // stockage disque ou mΘmoire
} else { // rΘception d'un en-tΩte octet par octet
int count=256;
int tot_nl=0;
int lf_detected=0;
int at_begining=1;
do {
nl=-1;
count--;
if (r->adr==NULL) {
r->adr=(char*) malloct(8192);
r->size=0;
}
if (r->adr!=NULL) {
if (r->size < 8190) {
// lecture
nl = hts_read(r,r->adr+r->size,1);
if (nl>0) {
// exit if:
// lf detected AND already detected before
// or
// lf detected AND first character read
if (*(r->adr+r->size) == 10) {
if (lf_detected || (at_begining) || (bufl<0))
count=-1;
lf_detected=1;
}
if (*(r->adr+r->size) != 13) { // sauter caractΦres 13
if (
(*(r->adr+r->size) != 10)
&&
(*(r->adr+r->size) != 13)
) {
// restart for new line
lf_detected=0;
}
(r->size)++;
at_begining=0;
}
*(r->adr+r->size)='\0'; // terminer par octet nul
}
}
}
if (nl >= 0) {
tot_nl+=nl;
if (!check_readinput(r))
count=-1;
}
} while((nl >= 0) && (count>0));
nl = tot_nl;
}
#if HDEBUG
//printf("add to %d / %d\n",r->size,r->totalsize);
#endif
// nl == 0 may mean "no relevant data", for example is using cache or ssl
#if HTS_USEOPENSSL
if (r->ssl)
return nl;
else
#endif
return ((nl > 0) ? nl : -1); // ==0 is fatal if direct read
}
// teste une adresse, et suit l'Θventuel chemin "moved"
// retourne 200 ou le code d'erreur (404=NOT FOUND, etc)
// copie dans loc la vΘritable adresse si celle-ci est diffΘrente
htsblk http_location(char* adr,char* fil,char* loc) {
htsblk retour;
int retry=0;
int tryagain;
// note: "RFC says"
// 5 boucles au plus, on en teste au plus 8 ici
// sinon abandon..
do {
tryagain=0;
switch ((retour=http_test(adr,fil,loc)).statuscode) {
case 200: break; // ok!
case 301: case 302: case 303: case 307: // moved!
// recalculer adr et fil!
if (ident_url_absolute(loc,adr,fil)!=-1) {
tryagain=1; // retenter
retry++; // ..encore une fois
}
}
} while((tryagain) && (retry<5+3));
return retour;
}
// teste si une URL (validitΘ, header, taille)
// retourne 200 ou le code d'erreur (404=NOT FOUND, etc)
// en cas de moved xx, dans location
// abandonne dΘsormais au bout de 30 secondes (aurevoir les sites
// qui nous font poireauter 5 heures..) -> -2=timeout
htsblk http_test(char* adr,char* fil,char* loc) {
T_SOC soc;
htsblk retour;
//int rcvsize=-1;
//char* rcv=NULL; // adresse de retour
//int bufl=TAILLE_BUFFER; // 8Ko de buffer
TStamp tl;
int timeout=30; // timeout pour un check (arbitraire) // **
// pour abandonner un site trop lent
tl=time_local();
loc[0]='\0';
memset(&retour, 0, sizeof(htsblk)); // effacer
retour.location=loc; // si non nul, contiendra l'adresse vΘritable en cas de moved xx
//soc=http_fopen(adr,fil,&retour,NULL); // ouvrir, + header
// on ouvre en head, et on traite l'en tΩte
soc=http_xfopen(1,0,1,NULL,adr,fil,&retour); // ouvrir HEAD, + envoi header
if (soc!=INVALID_SOCKET) {
int e=0;
// tant qu'on a des donnΘes, et qu'on ne recoit pas deux LF, et que le timeout n'arrie pas
do {
if (http_xfread1(&retour,0) < 0)
e=1;
else {
if (retour.adr!=NULL) {
if ((retour.adr[retour.size-1]!=10) || (retour.adr[retour.size-2]!=10))
e=1;
}
}
if (!e) {
if ((time_local()-tl)>=timeout) {
e=-1;
}
}
} while (!e);
if (e==1) {
if (adr!=NULL) {
int ptr=0;
char rcvd[1100];
// note: en gros recopie du traitement de back_wait()
//
// ----------------------------------------
// traiter en-tΩte!
// status-line α rΘcupΘrer
ptr+=binput(retour.adr+ptr,rcvd,1024);
if (strnotempty(rcvd)==0)
ptr+=binput(retour.adr+ptr,rcvd,1024); // "certains serveurs buggΘs envoient un \n au dΘbut" (RFC)
// traiter status-line
treatfirstline(&retour,rcvd);
#if HDEBUG
printf("(Buffer) Status-Code=%d\n",retour.statuscode);
#endif
// en-tΩte
// header // ** !attention! HTTP/0.9 non supportΘ
do {
ptr+=binput(retour.adr+ptr,rcvd,1024);
#if HDEBUG
printf("(buffer)>%s\n",rcvd);
#endif
if (strnotempty(rcvd))
treathead(NULL,NULL,NULL,&retour,rcvd); // traiter
} while(strnotempty(rcvd));
// ----------------------------------------
// libΘrer mΘmoire
if (retour.adr!=NULL) { freet(retour.adr); retour.adr=NULL; }
}
} else {
retour.statuscode=-2;
strcpy(retour.msg,"Timeout While Testing");
}
#if HTS_DEBUG_CLOSESOCK
DEBUG_W("http_test: deletehttp\n");
#endif
deletehttp(&retour);
retour.soc=INVALID_SOCKET;
}
return retour;
}
// CrΘe un lien (http) vers une adresse internet iadr
// retour: structure (adresse, taille, message si erreur (si !adr))
// peut ouvrir avec des connect() non bloquants: waitconnect=0/1
int newhttp(char* _iadr,htsblk* retour,int port,int waitconnect) {
t_fullhostent fullhostent_buffer; // buffer pour resolver
T_SOC soc; // descipteur de la socket
char* iadr;
// unsigned short int port;
// tester un Θventuel id:pass et virer id:pass@ si dΘtectΘ
iadr = jump_identification(_iadr);
// si iadr="#" alors c'est une fausse URL, mais un vrai fichier
// local.
// utile pour les tests!
//## if (iadr[0]!=lOCAL_CHAR) {
if (strcmp(iadr,"file://")) { /* non fichier */
SOCaddr server;
int server_size=sizeof(server);
t_hostent* hp;
// effacer structure
memset(&server, 0, sizeof(server));
#if HDEBUG
printf("gethostbyname\n");
#endif
// tester un Θventuel port
if (port==-1) {
char *a=jump_toport(iadr);
#if HTS_USEOPENSSL
if (retour->ssl)
port=443;
else
port=80; // port par dΘfaut
#else
port=80; // port par dΘfaut
#endif
if (a) {
char iadr2[HTS_URLMAXSIZE*2];
int i=-1;
iadr2[0]='\0';
sscanf(a+1,"%d",&i);
if (i!=-1) {
port=(unsigned short int) i;
}
// adresse vΘritable (sans :xx)
strncat(iadr2,iadr,(int) (a - iadr));
// adresse sans le :xx
hp = hts_gethostbyname(iadr2, &fullhostent_buffer);
} else {
// adresse normale (port par dΘfaut par la suite)
hp = hts_gethostbyname(iadr, &fullhostent_buffer);
}
} else // port dΘfini
hp = hts_gethostbyname(iadr, &fullhostent_buffer);
// Conversion iadr -> adresse
// structure recevant le nom de l'h⌠te, etc
//struct hostent *hp;
if (hp == NULL) {
#if DEBUG
printf("erreur gethostbyname\n");
#endif
if (retour)
if (retour->msg)
strcpy(retour->msg,"Unable to get server's address");
return INVALID_SOCKET;
}
// copie adresse
SOCaddr_copyaddr(server, server_size, hp->h_addr_list[0], hp->h_length);
// memcpy(&SOCaddr_sinaddr(server), hp->h_addr_list[0], hp->h_length);
// crΘer ("attachement") une socket (point d'accΦs) internet,en flot
#if HDEBUG
printf("socket\n");
#endif
#if HTS_WIDE_DEBUG
DEBUG_W("socket\n");
#endif
soc=socket(SOCaddr_sinfamily(server), SOCK_STREAM, 0);
#if HTS_WIDE_DEBUG
DEBUG_W("socket done\n");
#endif
if (soc==INVALID_SOCKET) {
if (retour)
if (retour->msg)
strcpy(retour->msg,"Unable to create a socket");
return INVALID_SOCKET; // erreur crΘation socket impossible
}
// structure: connexion au domaine internet, port 80 (ou autre)
SOCaddr_initport(server, port);
#if HDEBUG
printf("==%d\n",soc);
#endif
// connexion non bloquante?
if (!waitconnect ) {
unsigned long p=1; // non bloquant
#if HTS_WIN
ioctlsocket(soc,FIONBIO,&p);
#else
ioctl(soc,FIONBIO,&p);
#endif
}
// Connexion au serveur lui mΩme
#if HDEBUG
printf("connect\n");
#endif
#if HTS_WIDE_DEBUG
DEBUG_W("connect\n");
#endif
#if HTS_WIN
if (connect(soc, (const struct sockaddr FAR *)&server, server_size) != 0) {
#else
if (connect(soc, (struct sockaddr *)&server, server_size) == -1) {
#endif
// no - non blocking
//deletesoc(soc);
//soc=INVALID_SOCKET;
// bloquant
if (waitconnect) {
#if HDEBUG
printf("unable to connect!\n");
#endif
if (retour)
if (retour->msg)
strcpy(retour->msg,"Unable to connect to the server");
/* Close the socket and notify the error!!! */
deletesoc(soc);
return INVALID_SOCKET;
}
}
#if HTS_WIDE_DEBUG
DEBUG_W("connect done\n");
#endif
#if HDEBUG
printf("connexion Θtablie\n");
#endif
// A partir de maintenant, on peut envoyer et recevoir des donnΘes
// via le flot identifiΘ par soc (socket): write(soc,adr,taille) et
// read(soc,adr,taille)
} else { // on doit ouvrir un fichier local!
// il sera gΘrΘ de la mΩme maniΦre qu'une socket (c'est idem!)
soc=LOCAL_SOCKET_ID; // pseudo-socket locale..
// soc sera remplacΘ lors d'un http_fopen() par un handle vΘritable!
} // teste fichier local ou http
return soc;
}
// couper http://www.truc.fr/pub/index.html -> www.truc.fr /pub/index.html
// retour=-1 si erreur.
// si file://... alors adresse=file:// (et coupe le ?query dans ce cas)
int ident_url_absolute(char* url,char* adr,char* fil) {
int pos=0;
int scheme=0;
// effacer adr et fil
adr[0]=fil[0]='\0';
#if HDEBUG
printf("protocol: %s\n",url);
#endif
// Scheme?
{
char* a=url;
while (isalpha((unsigned char)*a))
a++;
if (*a == ':')
scheme=1;
}
// 1. optional scheme ":"
if ((pos=strfield(url,"file:"))) { // fichier local!! (pour les tests)
//!! p+=3;
strcpy(adr,"file://");
} else if ((pos=strfield(url,"http:"))) { // HTTP
//!!p+=3;
} else if ((pos=strfield(url,"ftp:"))) { // FTP
strcpy(adr,"ftp://"); // FTP!!
//!!p+=3;
#if HTS_USEOPENSSL
} else if ((pos=strfield(url,"https:"))) { // HTTPS
strcpy(adr,"https://");
#endif
} else if (scheme) {
return -1; // erreur non reconnu
} else
pos=0;
// 2. optional "//" authority
if (strncmp(url+pos,"//",2)==0)
pos+=2;
// (url+pos) now points to the path (not net path)
//## if (adr[0]!=lOCAL_CHAR) { // adresse normale http
if (!strfield(adr,"file:")) { // PAS file://
char *p,*q;
p=url+pos;
// p pointe sur le dΘbut de l'adresse, ex: www.truc.fr/sommaire/index.html
q=strchr(jump_identification(p),'/');
if (q==0) q=strchr(jump_identification(p),'?'); // http://www.foo.com?bar=1
if (q==0) q=p+strlen(p); // pointe sur \0
// q pointe sur le chemin, ex: index.html?query=recherche
// chemin www... trop long!!
if ( ( ((int) (q - p)) ) > HTS_URLMAXSIZE) {
//strcpy(retour.msg,"Path too long");
return -1; // erreur
}
// recopier adresse www..
strncat(adr,p, ((int) (q - p)) );
// *( adr+( ((int) q) - ((int) p) ) )=0; // faut arrΩter la fumette!
// recopier chemin /pub/..
if (q[0] != '/') // page par dΘfaut (/)
strcat(fil,"/");
strcat(fil,q);
// SECURITE:
// simplifier url pour les ../
fil_simplifie(fil);
} else { // localhost file://
char *p;
int i;
char* a;
p=url+pos;
strcat(fil,p); // fichier local ; adr="#"
a=strchr(fil,'?');
if (a)
*a='\0'; /* couper query (inutile pour file:// lors de la requΩte) */
// filtrer les \\ -> / pour les fichiers DOS
for(i=0;i<(int) strlen(fil);i++)
if (fil[i]=='\\')
fil[i]='/';
}
// no hostname
if (!strnotempty(adr))
return -1; // erreur non reconnu
// nommer au besoin.. (non utilisΘ normalement)
if (!strnotempty(fil))
strcpy(fil,"default-index.html");
// case insensitive pour adresse
{
char *a=jump_identification(adr);
while(*a) {
if ((*a>='A') && (*a<='Z'))
*a+='a'-'A';
a++;
}
}
return 0;
}
// simplification des ../
void fil_simplifie(char* f) {
int i=0;
int last=0;
char* a;
// Θliminer ../
while (f[i]) {
if (f[i]=='/') {
if (f[i+1]=='.')
if (f[i+2]=='.') // couper dernier rΘpertoire
if (f[i+3]=='/') // Θviter les /tmp/..coolandlamedir/
{ // couper dernier rΘpertoire
char tempo[HTS_URLMAXSIZE*2];
tempo[0]='\0';
//
if (!last) /* can't go upper.. */
strcpy(tempo,"/");
else
strncpy(tempo,f,last+1);
tempo[last+1]='\0';
strcat(tempo,f+i+4);
strcpy(f,tempo); // remplacer
i=-1; // recommencer
last=0;
}
if (i>=0)
last=i;
else
last=0;
}
i++;
}
// Θliminer ./
while ( (a=strstr(f,"./")) ) {
char tempo[HTS_URLMAXSIZE*2];
tempo[0]='\0';
strcpy(tempo,a+2);
strcpy(a,tempo);
}
// delete all remaining ../ (potential threat)
while ( (a=strstr(f,"../")) ) {
char tempo[HTS_URLMAXSIZE*2];
tempo[0]='\0';
strcpy(tempo,a+3);
strcpy(a,tempo);
}
}
// fermer liaison fichier ou socket
HTS_INLINE void deletehttp(htsblk* r) {
#if HTS_DEBUG_CLOSESOCK
char info[256];
sprintf(info,"deletehttp: (htsblk*) %d\n",r);
DEBUG_W2(info);
#endif
if (r->soc!=INVALID_SOCKET) {
if (r->is_file) {
if (r->fp)
fclose(r->fp);
r->fp=NULL;
} else {
if (r->soc!=LOCAL_SOCKET_ID)
deletesoc_r(r);
}
r->soc=INVALID_SOCKET;
}
}
// fermer une socket
HTS_INLINE void deletesoc(T_SOC soc) {
if (soc!=INVALID_SOCKET) {
// J'ai plantΘ.. pas de shutdown
//#if HTS_WIDE_DEBUG
// DEBUG_W("shutdown\n");
//#endif
// shutdown(soc,2); // shutdown
//#if HTS_WIDE_DEBUG
// DEBUG_W("shutdown done\n");
//#endif
// Ne pas oublier de fermer la connexion avant de partir.. (plus propre)
#if HTS_WIDE_DEBUG
DEBUG_W("close\n");
#endif
#if HTS_WIN
closesocket(soc);
#else
close(soc);
#endif
#if HTS_WIDE_DEBUG
DEBUG_W("close done\n");
#endif
}
}
/* Will also clean other things */
HTS_INLINE void deletesoc_r(htsblk* r) {
#if HTS_USEOPENSSL
if (r->ssl_con) {
SSL_shutdown(r->ssl_con);
// SSL_CTX_set_quiet_shutdown(r->ssl_con->ctx, 1);
SSL_free(r->ssl_con);
r->ssl_con=NULL;
}
#endif
deletesoc(r->soc);
r->soc=INVALID_SOCKET;
}
// renvoi le nombre de secondes depuis 1970
HTS_INLINE TStamp time_local(void) {
return ((TStamp) time(NULL));
}
// number of millisec since 1970
HTS_INLINE TStamp mtime_local(void) {
#ifndef HTS_DO_NOT_USE_FTIME
struct timeb B;
ftime( &B );
return (TStamp) ( ((TStamp) B.time * (TStamp) 1000)
+ ((TStamp) B.millitm) );
#else
// not precise..
return (TStamp) ( ((TStamp) time_local() * (TStamp) 1000)
+ ((TStamp) 0) );
#endif
}
// convertit un nombre de secondes en temps (chaine)
void sec2str(char *st,TStamp t) {
int j,h,m,s;
j=(int) (t/(3600*24));
t-=((TStamp) j)*(3600*24);
h=(int) (t/(3600));
t-=((TStamp) h)*3600;
m=(int) (t/60);
t-=((TStamp) m)*60;
s=(int) t;
if (j>0)
sprintf(st,"%d days, %d hours %d minutes %d seconds",j,h,m,s);
else if (h>0)
sprintf(st,"%d hours %d minutes %d seconds",h,m,s);
else if (m>0)
sprintf(st,"%d minutes %d seconds",m,s);
else
sprintf(st,"%d seconds",s);
}
// idem, plus court (chaine)
void qsec2str(char *st,TStamp t) {
int j,h,m,s;
j=(int) (t/(3600*24));
t-=((TStamp) j)*(3600*24);
h=(int) (t/(3600));
t-=((TStamp) h)*3600;
m=(int) (t/60);
t-=((TStamp) m)*60;
s=(int) t;
if (j>0)
sprintf(st,"%dd,%02dh,%02dmin%02ds",j,h,m,s);
else if (h>0)
sprintf(st,"%dh,%02dmin%02ds",h,m,s);
else if (m>0)
sprintf(st,"%dmin%02ds",m,s);
else
sprintf(st,"%ds",s);
}
// heure actuelle, GMT, format rfc (taille buffer 256o)
void time_gmt_rfc822(char* s) {
time_t tt;
struct tm* A;
tt=time(NULL);
A=gmtime(&tt);
if (A==NULL)
A=localtime(&tt);
time_rfc822(s,A);
}
// heure actuelle, format rfc (taille buffer 256o)
void time_local_rfc822(char* s) {
time_t tt;
struct tm* A;
tt=time(NULL);
A=localtime(&tt);
time_rfc822_local(s,A);
}
/* convertir une chaine en temps */
struct tm* convert_time_rfc822(char* s) {
struct tm* result;
/* */
char months[]="jan feb mar apr may jun jul aug sep oct nov dec";
char str[256];
char* a;
/* */
int result_mm=-1;
int result_dd=-1;
int result_n1=-1;
int result_n2=-1;
int result_n3=-1;
int result_n4=-1;
/* */
NOSTATIC_RESERVE(result, struct tm, 1);
if ((int) strlen(s) > 200)
return NULL;
strcpy(str,s);
hts_lowcase(str);
/* Θliminer :,- */
while( (a=strchr(str,'-')) ) *a=' ';
while( (a=strchr(str,':')) ) *a=' ';
while( (a=strchr(str,',')) ) *a=' ';
/* tokeniser */
a=str;
while(*a) {
char *first,*last;
char tok[256];
/* dΘcouper mot */
while(*a==' ') a++; /* sauter espaces */
first=a;
while((*a) && (*a!=' ')) a++;
last=a;
tok[0]='\0';
if (first!=last) {
char* pos;
strncat(tok,first,(int) (last - first));
/* analyser */
if ( (pos=strstr(months,tok)) ) { /* month always in letters */
result_mm=((int) (pos - months))/4;
} else {
int number;
if (sscanf(tok,"%d",&number) == 1) { /* number token */
if (result_dd<0) /* day always first number */
result_dd=number;
else if (result_n1<0)
result_n1=number;
else if (result_n2<0)
result_n2=number;
else if (result_n3<0)
result_n3=number;
else if (result_n4<0)
result_n4=number;
} /* sinon, bruit de fond(+1GMT for exampel) */
}
}
}
if ((result_n1>=0) && (result_mm>=0) && (result_dd>=0) && (result_n2>=0) && (result_n3>=0) && (result_n4>=0)) {
if (result_n4>=1000) { /* Sun Nov 6 08:49:37 1994 */
result->tm_year=result_n4-1900;
result->tm_hour=result_n1;
result->tm_min=result_n2;
result->tm_sec=max(result_n3,0);
} else { /* Sun, 06 Nov 1994 08:49:37 GMT or Sunday, 06-Nov-94 08:49:37 GMT */
result->tm_hour=result_n2;
result->tm_min=result_n3;
result->tm_sec=max(result_n4,0);
if (result_n1<=50) /* 00 means 2000 */
result->tm_year=result_n1+100;
else if (result_n1<1000) /* 99 means 1999 */
result->tm_year=result_n1;
else /* 2000 */
result->tm_year=result_n1-1900;
}
result->tm_isdst=0; /* assume GMT */
result->tm_yday=-1; /* don't know */
result->tm_wday=-1; /* don't know */
result->tm_mon=result_mm;
result->tm_mday=result_dd;
return result;
}
return NULL;
}
/* sets file time. -1 if error */
int set_filetime(char* file,struct tm* tm_time) {
struct utimbuf tim;
#ifndef HTS_DO_NOT_USE_FTIME
struct timeb B;
B.timezone=0;
ftime( &B );
tim.actime=tim.modtime=mktime(tm_time) - B.timezone*60;
#else
// bogus time (GMT/local)..
tim.actime=tim.modtime=mktime(tm_time);
#endif
return utime(file,&tim);
}
/* sets file time from RFC822 date+time, -1 if error*/
int set_filetime_rfc822(char* file,char* date) {
struct tm* tm_s=convert_time_rfc822(date);
if (tm_s) {
return set_filetime(file,tm_s);
} else return -1;
}
// heure au format rfc (taille buffer 256o)
HTS_INLINE void time_rfc822(char* s,struct tm * A) {
strftime(s,256,"%a, %d %b %Y %H:%M:%S GMT",A);
}
// heure locale au format rfc (taille buffer 256o)
HTS_INLINE void time_rfc822_local(char* s,struct tm * A) {
strftime(s,256,"%a, %d %b %Y %H:%M:%S",A);
}
// conversion en b,Kb,Mb
char* int2bytes(LLint n) {
char** a=int2bytes2(n);
char* buff;
NOSTATIC_RESERVE(buff, char, 256);
strcpy(buff,a[0]);
strcat(buff,a[1]);
return concat(buff,"");
}
// conversion en b/s,Kb/s,Mb/s
char* int2bytessec(long int n) {
char* buff;
char** a=int2bytes2(n);
NOSTATIC_RESERVE(buff, char, 256);
strcpy(buff,a[0]);
strcat(buff,a[1]);
return concat(buff,"/s");
}
char* int2char(int n) {
char* buffer;
NOSTATIC_RESERVE(buffer, char, 32);
sprintf(buffer,"%d",n);
return concat(buffer,"");
}
// conversion en b,Kb,Mb, nombre et type sΘparΘs
// limite: 2.10^9.10^6B
/* See http://physics.nist.gov/cuu/Units/binary.html */
#define ToLLint(a) ((LLint)(a))
#define ToLLintKiB (ToLLint(1024))
#define ToLLintMiB (ToLLintKiB*ToLLintKiB)
#ifdef HTS_LONGLONG
#define ToLLintGiB (ToLLintKiB*ToLLintKiB*ToLLintKiB)
#define ToLLintTiB (ToLLintKiB*ToLLintKiB*ToLLintKiB*ToLLintKiB)
#define ToLLintPiB (ToLLintKiB*ToLLintKiB*ToLLintKiB*ToLLintKiB*ToLLintKiB)
#endif
typedef struct {
char buff1[256];
char buff2[32];
char* buffadr[2];
} strc_int2bytes2;
char** int2bytes2(LLint n) {
strc_int2bytes2* strc;
NOSTATIC_RESERVE(strc, strc_int2bytes2, 1);
if (n < ToLLintKiB) {
sprintf(strc->buff1,"%d",(int)(LLint)n);
strcpy(strc->buff2,"B");
} else if (n < ToLLintMiB) {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/ToLLintKiB)),(int)((LLint)((n%ToLLintKiB)*100)/ToLLintKiB));
strcpy(strc->buff2,"KiB");
}
#ifdef HTS_LONGLONG
else if (n < ToLLintGiB) {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/(ToLLintMiB))),(int)((LLint)(((n%(ToLLintMiB))*100)/(ToLLintMiB))));
strcpy(strc->buff2,"MiB");
} else if (n < ToLLintTiB) {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/(ToLLintGiB))),(int)((LLint)(((n%(ToLLintGiB))*100)/(ToLLintGiB))));
strcpy(strc->buff2,"GiB");
} else if (n < ToLLintPiB) {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/(ToLLintTiB))),(int)((LLint)(((n%(ToLLintTiB))*100)/(ToLLintTiB))));
strcpy(strc->buff2,"TiB");
} else {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/(ToLLintPiB))),(int)((LLint)(((n%(ToLLintPiB))*100)/(ToLLintPiB))));
strcpy(strc->buff2,"PiB");
}
#else
else {
sprintf(strc->buff1,"%d,%02d",(int)((LLint)(n/(ToLLintMiB))),(int)((LLint)(((n%(ToLLintMiB))*100)/(ToLLintMiB))));
strcpy(strc->buff2,"MiB");
}
#endif
strc->buffadr[0]=strc->buff1;
strc->buffadr[1]=strc->buff2;
return strc->buffadr;
}
#if HTS_WIN
#else
// ignore sigpipe?
int sig_ignore_flag( int setflag ) { // flag ignore
static int flag=0; /* YES, this one is true static */
if (setflag>=0)
flag=setflag;
return flag;
}
#endif
// envoi de texte (en tΩtes gΘnΘralement) sur la socket soc
HTS_INLINE int sendc(htsblk* r, char* s) {
int n;
#if HTS_WIN
#else
sig_ignore_flag(1);
#endif
#if HDEBUG
write(0,s,strlen(s));
#endif
#if HTS_USEOPENSSL
if (r->ssl) {
n = SSL_write(r->ssl_con, s, strlen(s));
} else
#endif
n = send(r->soc,s,strlen(s),0);
#if HTS_WIN
#else
sig_ignore_flag(0);
#endif
return n;
}
// Remplace read
void finput(int fd,char* s,int max) {
char c;
int j=0;
do {
//c=fgetc(fp);
if (read(fd,&c,1)<=0) {
c=0;
}
if (c!=0) {
switch(c) {
case 10: c=0; break;
case 13: break; // sauter ces caractΦres
default: s[j++]=c; break;
}
}
} while((c!=0) && (j<max-1));
s[j++]='\0';
}
// Like linput, but in memory (optimized)
int binput(char* buff,char* s,int max) {
char* end;
int count;
// clear buffer
s[0]='\0';
// end of buffer?
if ( *buff == '\0')
return 0;
// find ending \n
end=strchr(buff,'\n');
// ..or end of buffer
if (!end)
end=buff+strlen(buff);
// then count number of bytes, maximum=max
count=min(max,end-buff);
// and strip annoying ending cr
while( (count>0) && (buff[count] == '\r'))
count--;
// copy
memcpy(s, buff, count); // strncat(s,buff,count);
// and terminate with a null char
s[count]='\0';
// then return the supplemental jump offset
return (end-buff)+1;
}
// Lecture d'une ligne (peut Ωtre unicode α priori)
int linput(FILE* fp,char* s,int max) {
int c;
int j=0;
do {
c=fgetc(fp);
if (c!=EOF) {
switch(c) {
case 13: break; // sauter CR
case 10: c=-1; break;
case 9: case 12: break; // sauter ces caractΦres
default: s[j++]=(char) c; break;
}
}
} while((c!=-1) && (c!=EOF) && (j<(max-1)));
s[j]='\0';
return j;
}
int linput_trim(FILE* fp,char* s,int max) {
int rlen=0;
char* ls=(char*) malloct(max+2);
s[0]='\0';
if (ls) {
char* a;
// lire ligne
rlen=linput(fp,ls,max);
if (rlen) {
// sauter espaces et tabs en fin
while( (rlen>0) && ((ls[max(rlen-1,0)]==' ') || (ls[max(rlen-1,0)]=='\t')) )
ls[--rlen]='\0';
// sauter espaces en dΘbut
a=ls;
while((rlen>0) && ((*a==' ') || (*a=='\t'))) {
a++;
rlen--;
}
if (rlen>0) {
memcpy(s,a,rlen); // can copy \0 chars
s[rlen]='\0';
}
}
//
freet(ls);
}
return rlen;
}
int linput_cpp(FILE* fp,char* s,int max) {
int rlen=0;
s[0]='\0';
do {
int ret;
if (rlen>0)
if (s[rlen-1]=='\\')
s[--rlen]='\0'; // couper \ final
// lire ligne
ret=linput_trim(fp,s+rlen,max-rlen);
if (ret>0)
rlen+=ret;
} while((s[max(rlen-1,0)]=='\\') && (rlen<max));
return rlen;
}
// idem avec les car spΘciaux
void rawlinput(FILE* fp,char* s,int max) {
int c;
int j=0;
do {
c=fgetc(fp);
if (c!=EOF) {
switch(c) {
case 13: break; // sauter CR
case 10: c=-1; break;
default: s[j++]=(char) c; break;
}
}
} while((c!=-1) && (c!=EOF) && (j<(max-1)));
s[j++]='\0';
}
// compare le dΘbut de f avec s et retourne la position de la fin
// 'A=a' (case insensitive)
int strfield(const char* f,const char* s) {
int r=0;
while (streql(*f,*s) && ((*f)!=0) && ((*s)!=0)) { f++; s++; r++; }
if (*s==0)
return r;
else
return 0;
}
//cherche chaine, case insensitive
char* strstrcase(char *s,char *o) {
while((*s) && (strfield(s,o)==0)) s++;
if (*s=='\0') return NULL;
return s;
}
// Unicode detector
// See http://www.unicode.org/unicode/reports/tr28/
// (sect Table 3.1B. Legal UTF-8 Byte Sequences)
typedef struct {
unsigned int pos;
unsigned char data[4];
} t_auto_seq;
// char between a and b
#define CHAR_BETWEEN(c, a, b) ( (c) >= 0x##a ) && ( (c) <= 0x##b )
// sequence start
#define SEQBEG ( inseq == 0 )
// in this block
#define BLK(n,a, b) ( (seq.pos >= n) && ((err = CHAR_BETWEEN(seq.data[n], a, b))) )
#define ELT(n,a) BLK(n,a,a)
// end
#define SEQEND ((ok = 1))
// sequence started, character will fail if error
#define IN_SEQ ( (inseq = 1) )
// decoding error
#define BAD_SEQ ( (ok == 0) && (inseq != 0) && (!err) )
// no sequence started
#define NO_SEQ ( inseq == 0 )
// is this block an UTF unicode textfile?
// 0 : no
// 1 : yes
// -1: don't know
int is_unicode_utf8(unsigned char* buffer, unsigned int size) {
t_auto_seq seq;
unsigned int i;
int is_utf=-1;
seq.pos=0;
for(i=0 ; i < size ; i++) {
unsigned int ok=0;
unsigned int inseq=0;
unsigned int err=0;
seq.data[seq.pos]=buffer[i];
/**/ if ( SEQBEG && BLK(0,00,7F) && IN_SEQ && SEQEND ) { }
else if ( SEQBEG && BLK(0,C2,DF) && IN_SEQ && BLK(1,80,BF) && SEQEND ) { }
else if ( SEQBEG && ELT(0,E0 ) && IN_SEQ && BLK(1,A0,BF) && BLK(2,80,BF) && SEQEND ) { }
else if ( SEQBEG && BLK(0,E1,EC) && IN_SEQ && BLK(1,80,BF) && BLK(2,80,BF) && SEQEND ) { }
else if ( SEQBEG && ELT(0,ED ) && IN_SEQ && BLK(1,80,9F) && BLK(2,80,BF) && SEQEND ) { }
else if ( SEQBEG && BLK(0,EE,EF) && IN_SEQ && BLK(1,80,BF) && BLK(2,80,BF) && SEQEND ) { }
else if ( SEQBEG && ELT(0,F0 ) && IN_SEQ && BLK(1,90,BF) && BLK(2,80,BF) && BLK(3,80,BF) && SEQEND ) { }
else if ( SEQBEG && BLK(0,F1,F3) && IN_SEQ && BLK(1,80,BF) && BLK(2,80,BF) && BLK(3,80,BF) && SEQEND ) { }
else if ( SEQBEG && ELT(0,F4 ) && IN_SEQ && BLK(1,80,8F) && BLK(2,80,BF) && BLK(3,80,BF) && SEQEND ) { }
else if ( NO_SEQ ) { // bad, unknown
return 0;
}
/* */
/* Error */
if ( BAD_SEQ ) {
return 0;
}
/* unicode character */
if (seq.pos > 0)
is_utf=1;
/* Next */
if (ok)
seq.pos=0;
else
seq.pos++;
/* Internal error */
if (seq.pos >= 4)
return 0;
}
return is_utf;
}
// le fichier est-il un fichier html?
// 0 : non
// 1 : oui
// -1 : on sait pas
// -2 : on sait pas, pas d'extension
int ishtml(char* fil) {
char *a;
// patch pour les truc.html?Choix=toto
if ( (a=strchr(fil,'?')) ) // paramΦtres?
a--; // pointer juste avant le ?
else
a=fil+strlen(fil)-1; // pointer sur le dernier caractΦre
if (*a=='/') return -1; // rΘpertoire, on sait pas!!
//if (*a=='/') return 1; // ok rΘpertoire, html
while ( (*a!='.') && (*a!='/') && ( a > fil)) a--;
if (*a=='.') { // a une extension
char fil_noquery[HTS_URLMAXSIZE*2];
fil_noquery[0]='\0';
a++; // pointer sur extension
strncat(fil_noquery,a,HTS_URLMAXSIZE);
a=strchr(fil_noquery,'?');
if (a)
*a='\0';
return ishtml_ext(fil_noquery); // retour
} else return -2; // indΘterminΘ, par exemple /truc
}
// idem, mais pour uniquement l'extension
int ishtml_ext(char* a) {
int html=0;
//
if (strfield2(a,"html")) html = 1;
else if (strfield2(a,"htm")) html = 1;
else if (strfield2(a,"shtml")) html = 1;
else if (strfield2(a,"phtml")) html = 1;
else if (strfield2(a,"htmlx")) html = 1;
else if (strfield2(a,"shtm")) html = 1;
else if (strfield2(a,"phtm")) html = 1;
else if (strfield2(a,"htmx")) html = 1;
//
// insuccΦs..
else {
switch(is_knowntype(a)) {
case 1:
html = 0; // connu, non html
break;
case 2:
html = 1; // connu, html
break;
default:
html = -1; // inconnu..
break;
}
}
return html;
}
// error (404,500..)
HTS_INLINE int ishttperror(int err) {
switch (err/100) {
case 4: case 5: return 1;
break;
}
return 0;
}
// retourne le pointeur ou le pointeur + offset si il existe dans la chaine un @ signifiant
// une identification
char* jump_identification(char* source) {
char *a,*trytofind;
// rechercher dernier @ (car parfois email transmise dans adresse!)
// mais sauter ftp:// Θventuel
a = jump_protocol(source);
trytofind = strrchr_limit(a, '@', strchr(a,'/'));
return (trytofind != NULL)?trytofind:a;
}
// find port (:80) or NULL if not found
// can handle IPV6 addresses
char* jump_toport(char* source) {
char *a,*trytofind;
a = jump_identification(source);
trytofind = strrchr_limit(a, ']', strchr(source, '/')); // find last ] (http://[3ffe:b80:1234::1]:80/foo.html)
a = strchr( (trytofind)?trytofind:a, ':');
return a;
}
// strrchr, but not too far
char* strrchr_limit(char* s, char c, char* limit) {
if (limit == NULL) {
char* p = strchr(s, c);
return p?(p+1):NULL;
} else {
char *a=NULL, *p;
for(;;) {
p=strchr((a)?a:s, c);
if ((p >= limit) || (p == NULL))
return a;
a=p+1;
}
}
}
// retourner adr sans ftp://
HTS_INLINE char* jump_protocol(char* source) {
int p;
// scheme
// "Comparisons of scheme names MUST be case-insensitive" (RFC2616)
if ((p=strfield(source,"http:")))
source+=p;
else if ((p=strfield(source,"ftp:")))
source+=p;
#if HTS_USEOPENSSL
else if ((p=strfield(source,"https:")))
source+=p;
#endif
// net_path
if (strncmp(source,"//",2)==0)
source+=2;
return source;
}
// codage base 64 a vers b
void code64(char* a,char* b) {
int i1=0,i2=0,i3=0,i4=0;
unsigned long store;
int n;
const char _hts_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
b[0]='\0';
while(*a) {
// 24 bits
n=1; store=0; store |= ((*a++) & 0xff);
if (*a) { n=2; store <<= 8; store |= ((*a++) & 0xff); }
if (*a) { n=3; store <<= 8; store |= ((*a++) & 0xff); }
if (n==3) {
i4=store & 63;
i3=(store>>6) & 63;
i2=(store>>12) & 63;
i1=(store>>18) & 63;
} else if (n==2) {
store<<=2;
i3=store & 63;
i2=(store>>6) & 63;
i1=(store>>12) & 63;
} else {
store<<=4;
i2=store & 63;
i1=(store>>6) & 63;
}
*b++ = _hts_base64[i1];
*b++ = _hts_base64[i2];
if (n>=2)
*b++ = _hts_base64[i3];
else
*b++ = '=';
if (n>=3)
*b++ = _hts_base64[i4];
else
*b++ = '=';
}
*b++='\0';
}
// remplacer " par " etc..
// buffer MAX 1Ko
void unescape_amp(char* s) {
while(*s) {
if (*s=='&') {
char* end=strchr(s,';');
if ( ((int) (end - s)) <= 8) {
char c=0;
if (strfield(s,"&"))
c='&';
else if (strfield(s,"°"))
c='░';
else if (strfield(s,">"))
c='>';
else if (strfield(s,"«"))
c='\"';
else if (strfield(s,"<"))
c='<';
else if (strfield(s," "))
c=' ';
else if (strfield(s,"""))
c='\"';
else if (strfield(s,"»"))
c='\"';
else if (strfield(s,""))
c='-';
else if (strfield(s,"˜"))
c='~';
else if (strfield(s,"&"))
c='&';
// remplacer?
if (c) {
char buff[HTS_URLMAXSIZE*2];
buff[0]=c;
strcpy(buff+1,end+1);
strcpy(s,buff);
}
}
}
s++;
}
}
// remplacer %20 par ' ', | par : etc..
// buffer MAX 1Ko
char* unescape_http(char* s) {
char* tempo;
int i,j=0;
NOSTATIC_RESERVE(tempo, char, HTS_URLMAXSIZE*2);
for (i=0;i<(int) strlen(s);i++) {
if (s[i]=='%') {
i++;
tempo[j++]=(char) ehex(s+i);
i++; // sauter 2 caractΦres finalement
}
/*
NON a cause de trucs comme /home/0,1837,1|7|1173|Content,00.html
else if (s[i]=='|') { // exemple: file:///C|Program%20Files...
tempo[j++]=':';
}
*/
else
tempo[j++]=s[i];
}
tempo[j++]='\0';
return tempo;
}
// unescape in URL/URI ONLY what has to be escaped, to form a standard URL/URI
char* unescape_http_unharm(char* s, int no_high) {
char* tempo;
int i,j=0;
NOSTATIC_RESERVE(tempo, char, HTS_URLMAXSIZE*2);
for (i=0;i<(int) strlen(s);i++) {
if (s[i]=='%') {
int nchar=(char) ehex(s+i+1);
int test = ( CHAR_RESERVED(nchar)
|| CHAR_DELIM(nchar)
|| CHAR_UNWISE(nchar)
|| CHAR_LOW(nchar) /* CHAR_SPECIAL */
|| CHAR_XXAVOID(nchar)
|| (
(no_high)
&&
CHAR_HIG(nchar)
)
);
if (!test) {
tempo[j++]=(char) ehex(s+i+1);
i+=2;
} else {
tempo[j++]='%';
}
}
/*
NON a cause de trucs comme /home/0,1837,1|7|1173|Content,00.html
else if (s[i]=='|') { // exemple: file:///C|Program%20Files...
tempo[j++]=':';
}
*/
else
tempo[j++]=s[i];
}
tempo[j++]='\0';
return tempo;
}
// remplacer " par %xx etc..
// buffer MAX 1Ko
void escape_spc_url(char* s) {
x_escape_http(s,2);
}
// smith / john -> smith%20%2f%20john
void escape_in_url(char* s) {
x_escape_http(s,1);
}
// smith / john -> smith%20/%20john
void escape_uri(char* s) {
x_escape_http(s,3);
}
void escape_uri_utf(char* s) {
x_escape_http(s,30);
}
void escape_check_url(char* s) {
x_escape_http(s,0);
}
// same as escape_check_url, but returns char*
char* escape_check_url_addr(char* s) {
char* adr;
escape_check_url(adr = concat(s,""));
return adr;
}
void x_escape_http(char* s,int mode) {
while(*s) {
int test=0;
if (mode == 0)
test=(strchr("\" ",*s)!=0);
else if (mode==1) {
test = ( CHAR_RESERVED(*s)
|| CHAR_DELIM(*s)
|| CHAR_UNWISE(*s)
|| CHAR_SPECIAL(*s)
|| CHAR_XXAVOID(*s) );
}
else if (mode==2)
test=(strchr(" ",*s)!=0); // n'escaper que espace
else if (mode==3) { // Θchapper que ce qui est nΘcessaire
test = (
CHAR_SPECIAL(*s)
|| CHAR_XXAVOID(*s) );
}
else if (mode==30) { // Θchapper que ce qui est nΘcessaire
test = (
CHAR_LOW(*s)
|| CHAR_XXAVOID(*s) );
}
if (test) {
char buffer[HTS_URLMAXSIZE*2];
int n;
n=(int)(unsigned char) *s;
strcpy(buffer,s+1);
sprintf(s,"%%%02x",n);
strcat(s,buffer);
}
s++;
}
}
HTS_INLINE int ehexh(char c) {
if ((c>='0') && (c<='9')) return c-'0';
if ((c>='a') && (c<='f')) c-=('a'-'A');
if ((c>='A') && (c<='F')) return (c-'A'+10);
return 0;
}
HTS_INLINE int ehex(char* s) {
return 16*ehexh(*s)+ehexh(*(s+1));
}
// concat, concatΦne deux chaines et renvoi le rΘsultat
// permet d'allΘger grandement le code
// il faut savoir qu'on ne peut mettre plus de 16 concat() dans une expression
typedef struct {
char buff[16][HTS_URLMAXSIZE*2*2];
int rol;
} concat_strc;
char* concat(const char* a,const char* b) {
concat_strc* strc;
NOSTATIC_RESERVE(strc, concat_strc, 1);
strc->rol=((strc->rol+1)%16); // roving pointer
strcpy(strc->buff[strc->rol],a);
if (b) strcat(strc->buff[strc->rol],b);
return strc->buff[strc->rol];
}
// conversion fichier / -> antislash
#if HTS_DOSNAME
char* __fconv(char* a) {
int i;
for(i=0;i<(int) strlen(a);i++)
if (a[i]=='/') // convertir
a[i]='\\';
return a;
}
char* fconcat(char* a,char* b) {
return __fconv(concat(a,b));
}
char* fconv(char* a) {
return __fconv(concat(a,""));
}
#endif
/* / et \\ en / */
char* __fslash(char* a) {
int i;
for(i=0;i<(int) strlen(a);i++)
if (a[i]=='\\') // convertir
a[i]='/';
return a;
}
char* fslash(char* a) {
return __fslash(concat(a,""));
}
// conversion minuscules, avec buffer
char* convtolower(char* a) {
concat_strc* strc;
NOSTATIC_RESERVE(strc, concat_strc, 1);
strc->rol=((strc->rol+1)%16); // roving pointer
strcpy(strc->buff[strc->rol],a);
hts_lowcase(strc->buff[strc->rol]); // lower case
return strc->buff[strc->rol];
}
// conversion en minuscules
void hts_lowcase(char* s) {
int i;
for(i=0;i<(int) strlen(s);i++)
if ((s[i]>='A') && (s[i]<='Z'))
s[i]+=('a'-'A');
}
// remplacer un caractΦre d'une chaεne dans une autre
HTS_INLINE void hts_replace(char *s,char from,char to) {
char* a;
while ((a=strchr(s,from))!=NULL) {
*a=to;
}
}
// caractΦre espace, guillemets, CR, LF etc..
/* SECTION OPTIMISEE:
#define is_space(c) (strchr(" \"\x0d\x0a\x09'",c)!=NULL)
#define is_realspace(c) (strchr(" \x0d\x0a\x09",c)!=NULL)
*/
/*
HTS_INLINE int is_space(char c) {
if (c==' ') return 1; // spc
if (c=='"') return 1; // quote
if (c==10) return 1; // lf
if (c==13) return 1; // cr
if (c=='\'') return 1; // quote
//if (c=='`') return 1; // backquote << non
if (c==9) return 1; // tab
return 0;
}
*/
// caractΦre espace, CR, LF, TAB
/*
HTS_INLINE int is_realspace(char c) {
if (c==' ') return 1; // spc
if (c==10) return 1; // lf
if (c==13) return 1; // cr
if (c==9) return 1; // tab
return 0;
}
*/
// deviner type d'un fichier local..
// ex: fil="toto.gif" -> s="image/gif"
void guess_httptype(char *s,char *fil) {
get_httptype(s,fil,1);
}
// idem
// flag: 1 si toujours renvoyer un type
void get_httptype(char *s,char *fil,int flag) {
if (ishtml(fil)==1)
strcpy(s,"text/html");
else {
char *a=fil+strlen(fil)-1;
while ( (*a!='.') && (*a!='/') && (a>fil)) a--;
if (*a=='.') {
int ok=0;
int j=0;
a++;
while( (!ok) && (strnotempty(hts_mime[j][1])) ) {
if (strfield2(hts_mime[j][1],a)) {
if (hts_mime[j][0][0]!='*') { // Une correspondance existe
strcpy(s,hts_mime[j][0]);
ok=1;
}
}
j++;
}
if (!ok) if (flag) sprintf(s,"application/%s",a);
} else {
if (flag) strcpy(s,"application/octet-stream");
}
}
}
// get type of fil (php)
// s: buffer (text/html) or NULL
// return: 1 if known by user
int get_userhttptype(int setdefs,char *s,char *ext) {
char** buffer=NULL;
NOSTATIC_RESERVE(buffer, char*, 1);
if (setdefs) {
*buffer=s;
return 1;
} else {
if (s)
s[0]='\0';
if (!ext)
return 0;
if (*buffer) {
char search[1024];
char* detect;
sprintf(search,"\n%s=",ext); // php=text/html
detect=strstr(*buffer,search);
if (!detect) {
sprintf(search,"\n%s\n",ext); // php\ncgi=text/html
detect=strstr(*buffer,search);
}
if (detect) {
detect=strchr(detect,'=');
if (detect) {
detect++;
if (s) {
char* a;
a=strchr(detect,'\n');
if (a) {
strncat(s,detect,(int) (a - detect));
}
}
return 1;
}
}
}
}
return 0;
}
// renvoyer extesion d'un type mime..
// ex: "image/gif" -> gif
void give_mimext(char *s,char *st) {
int ok=0;
int j=0;
s[0]='\0';
while( (!ok) && (strnotempty(hts_mime[j][1])) ) {
if (strfield2(hts_mime[j][0],st)) {
if (hts_mime[j][1][0]!='*') { // Une correspondance existe
strcpy(s,hts_mime[j][1]);
ok=1;
}
}
j++;
}
// wrap "x" mimetypes, such as:
// application/x-mp3
// or
// application/mp3
if (!ok) {
int p;
char* a=NULL;
if ((p=strfield(st,"application/x-")))
a=st+p;
else if ((p=strfield(st,"application/")))
a=st+p;
if (a) {
if ((int)strlen(a) >= 1) {
if ((int)strlen(a) <= 4) {
strcpy(s,a);
ok=1;
}
}
}
}
}
// extension connue?..
// 0 : non
// 1 : oui
// 2 : html
int is_knowntype(char *fil) {
int j=0;
if (!fil)
return 0;
while(strnotempty(hts_mime[j][1])) {
if (strfield2(hts_mime[j][1],fil)) {
if (strfield2(hts_mime[j][0],"text/html"))
return 2;
else
return 1;
}
j++;
}
// Known by user?
return (is_userknowntype(fil));
}
// extension : html,gif..
char* get_ext(char *fil) {
char* fil_noquery;
char *a=fil+strlen(fil)-1;
NOSTATIC_RESERVE(fil_noquery, char, HTS_URLMAXSIZE*2);
while ( (*a!='.') && (*a!='/') && (a>fil)) a--;
if (*a=='.') {
fil_noquery[0]='\0';
a++; // pointer sur extension
strncat(fil_noquery,a,HTS_URLMAXSIZE);
a=strchr(fil_noquery,'?');
if (a)
*a='\0';
return concat(fil_noquery,"");
}
else
return "";
}
// known type?..
// 0 : no
// 1 : yes
// 2 : html
// setdefs : set mime buffer:
// file=(char*) "asp=text/html\nphp=text/html\n"
int is_userknowntype(char *fil) {
char mime[1024];
if (!fil)
return 0;
if (!strnotempty(fil))
return 0;
mime[0]='\0';
get_userhttptype(0,mime,fil);
if (!strnotempty(mime))
return 0;
else if (strfield2(mime,"text/html"))
return 2;
else
return 1;
}
// page dynamique?
// is_dyntype(get_ext("foo.asp"))
int is_dyntype(char *fil) {
int j=0;
if (!fil)
return 0;
if (!strnotempty(fil))
return 0;
while(strnotempty(hts_ext_dynamic[j])) {
if (strfield2(hts_ext_dynamic[j],fil)) {
return 1;
}
j++;
}
return 0;
}
// types critiques qui ne doivent pas Ωtre changΘs car renvoyΘs par des serveurs qui ne
// connaissent pas le type
int may_unknown(char* st) {
int j=0;
// types mΘdia
if (may_be_hypertext_mime(st))
return 1;
while(strnotempty(hts_mime_keep[j])) {
if (strfield2(hts_mime_keep[j],st)) { // trouvΘ
return 1;
}
j++;
}
return 0;
}
// -- Utils fichiers
// pretty print for i/o
void fprintfio(FILE* fp,char* buff,char* prefix) {
char nl=1;
while(*buff) {
switch(*buff) {
case 13: break;
case 10:
fprintf(fp,"\r\n");
nl=1;
break;
default:
if (nl)
fprintf(fp,prefix);
nl=0;
fputc(*buff,fp);
}
buff++;
}
}
/* Le fichier existe-t-il? (ou est-il accessible?) */
int fexist(char* s) {
FILE* fp;
if (strnotempty(s)==0) // nom vide: non trouvΘ
return 0;
fp=fopen(fconv(s),"rb");
if (fp!=NULL) fclose(fp);
return (fp!=NULL);
}
/* Taille d'un fichier, -1 si n'existe pas */
/* fp->_cnt ne fonctionne pas sur toute les plate-formes :-(( */
/* Note: NOT YET READY FOR 64-bit */
//LLint fsize(char* s) {
int fsize(char* s) {
/*
#if HTS_WIN
HANDLE hFile;
DWORD dwSizeHigh = 0;
DWORD dwSizeLow = 0;
hFile = CreateFile(s,0,0,NULL,OPEN_EXISTING,0,NULL);
if (hFile) {
dwSizeLow = GetFileSize (hFile, & dwSizeHigh) ;
CloseHandle(hFile);
if (dwSizeLow != 0xFFFFFFFF)
return (dwSizeLow & (dwSizeHigh<<32));
else
return -1;
} else
return -1;
#else
*/
FILE* fp;
if (strnotempty(s)==0) // nom vide: erreur
return -1;
fp=fopen(fconv(s),"rb");
if (fp!=NULL) {
int i;
fseek(fp,0,SEEK_END);
i=ftell(fp);
fclose(fp);
return i;
} else return -1;
/*
#endif
*/
}
int fpsize(FILE* fp) {
int oldpos,size;
if (!fp)
return -1;
oldpos=ftell(fp);
fseek(fp,0,SEEK_END);
size=ftell(fp);
fseek(fp,oldpos,SEEK_SET);
return size;
}
/* root dir, with ending / */
typedef struct {
char path[1024+4];
int init;
} hts_rootdir_strc;
char* hts_rootdir(char* file) {
hts_rootdir_strc* strc;
NOSTATIC_RESERVE(strc, hts_rootdir_strc, 1);
if (file) {
if (!strc->init) {
strc->path[0]='\0';
strc->init=1;
if (strnotempty(file)) {
char* a;
strcpy(strc->path,file);
while((a=strrchr(strc->path,'\\'))) *a='/';
if ((a=strrchr(strc->path,'/'))) {
*(a+1)='\0';
} else
strc->path[0]='\0';
}
if (!strnotempty(strc->path)) {
if( getcwd( strc->path, 1024 ) == NULL )
strc->path[0]='\0';
else
strcat(strc->path,"/");
}
}
return NULL;
} else if (strc->init)
return strc->path;
else
return "";
}
hts_stat_struct HTS_STAT;
//
// return number of downloadable bytes, depending on rate limiter
// see engine_stats() routine, too
// this routine works quite well for big files and regular ones, but apparently the rate limiter has
// some problems with very small files (rate too high)
LLint check_downloadable_bytes(int rate) {
if (rate>0) {
TStamp time_now;
TStamp elapsed_useconds;
LLint bytes_transfered_during_period;
LLint left;
// get the older timer
int id_timer = (HTS_STAT.istat_idlasttimer + 1) % 2;
time_now=mtime_local();
elapsed_useconds = time_now - HTS_STAT.istat_timestart[id_timer];
// NO totally stupid - elapsed_useconds+=1000; // for the next second, too
bytes_transfered_during_period = (HTS_STAT.HTS_TOTAL_RECV-HTS_STAT.istat_bytes[id_timer]);
left = ((rate * elapsed_useconds)/1000) - bytes_transfered_during_period;
if (left <= 0)
left = 0;
return left;
} else
return TAILLE_BUFFER;
}
//
// 0 : OK
// 1 : slow down
#if 0
int HTS_TOTAL_RECV_CHECK(int var) {
if (HTS_STAT.HTS_TOTAL_RECV_STATE)
return 1;
/*
{
if (HTS_STAT.HTS_TOTAL_RECV_STATE==3) {
var = min(var,32);
Sleep(250);
} else if (HTS_STAT.HTS_TOTAL_RECV_STATE==2) {
var = min(var,256);
Sleep(100);
} else {
var/=2;
if (var<=0) var=1;
Sleep(50);
}
}
*/
return 0;
}
#endif
// Lecture dans buff de size octets au maximum en utilisant la socket r (structure htsblk)
// >0 : data received
// == 0 : not yet data
// <0 : no more data or error
HTS_INLINE int hts_read(htsblk* r,char* buff,int size) {
int retour;
// return read(soc,buff,size);
if (r->is_file) {
#if HTS_WIDE_DEBUG
DEBUG_W("read\n");
#endif
if (r->fp)
retour=fread(buff,1,size,r->fp);
else
retour=-1;
} else {
#if HTS_WIDE_DEBUG
DEBUG_W("recv\n");
if (r->soc==INVALID_SOCKET)
printf("!!WIDE_DEBUG ERROR, soc==INVALID hts_read\n");
#endif
//HTS_TOTAL_RECV_CHECK(size); // Diminuer au besoin si trop de donnΘes reτues
#if HTS_USEOPENSSL
if (r->ssl) {
retour = SSL_read(r->ssl_con, buff, size);
if (retour <= 0) {
int err_code = SSL_get_error(r->ssl_con, retour);
if (
(err_code == SSL_ERROR_WANT_READ)
||
(err_code == SSL_ERROR_WANT_WRITE)
)
{
retour = 0; /* no data yet (ssl cache) */
} else {
retour = -1; /* eof or error */
}
}
} else {
#endif
retour=recv(r->soc,buff,size,0);
}
if (retour > 0) // compter flux entrant
HTS_STAT.HTS_TOTAL_RECV+=retour;
#if HTS_USEOPENSSL
}
#endif
#if HTS_WIDE_DEBUG
DEBUG_W("recv/read done\n");
#endif
return retour;
}
// -- Gestion cache DNS --
// 'RX98
#if HTS_DNSCACHE
// 'capsule' contenant uniquement le cache
t_dnscache* _hts_cache(void) {
t_dnscache* cache;
NOSTATIC_RESERVE(cache, t_dnscache, 1);
return cache;
}
// lock le cache dns pour tout opΘration d'ajout
// plus prudent quand plusieurs threads peuvent Θcrire dedans..
// -1: status? 0: libΘrer 1:locker
/*
Simple lock function for cache
Return value: always 0
Parameter:
1 wait for lock (mutex) available and lock it
0 unlock the mutex
[-1 check if locked (always return 0 with mutex)]
-999 initialize
*/
#if USE_BEGINTHREAD
int _hts_lockdns(int i) {
static PTHREAD_LOCK_TYPE hMutex;
return htsSetLock(&hMutex,i);
}
#else
int _hts_lockdns(int i) {
int l=0;
if (i>=0)
l=i;
return l;
}
#endif
// routine pour le cache - retour optionnel α donner α chaque fois
// NULL: nom non encore testΘ dans le cache
// si h_length==0 alors le nom n'existe pas dans le dns
t_hostent* _hts_ghbn(t_dnscache* cache,char* iadr,t_hostent* retour) {
// attendre que le cache dns soit prΩt
while(_hts_lockdns(-1)); // attendre libΘration
_hts_lockdns(1); // locker
while(1) {
if (strcmp(cache->iadr,iadr)==0) { // ok trouvΘ
if (cache->host_length>0) { // entrΘe valide
if (retour->h_addr_list[0])
memcpy(retour->h_addr_list[0], cache->host_addr, cache->host_length);
retour->h_length=cache->host_length;
} else if (cache->host_length==0) { // en cours
_hts_lockdns(0); // dΘlocker
return NULL;
} else { // erreur dans le dns, dΘja vΘrifiΘ
if (retour->h_addr_list[0])
retour->h_addr_list[0][0]='\0';
retour->h_length=0; // erreur, n'existe pas
}
_hts_lockdns(0); // dΘlocker
return retour;
} else { // on a pas encore trouvΘ
if (cache->n!=NULL) { // chercher encore
cache=cache->n; // suivant!
} else {
_hts_lockdns(0); // dΘlocker
return NULL; // non prΘsent
}
}
}
}
// tester si iadr a dΘja ΘtΘ testΘ (ou en cours de test)
// 0 non encore
// 1 ok
// 2 non prΘsent
int hts_dnstest(char* _iadr) {
char* iadr;
t_dnscache* cache=_hts_cache(); // adresse du cache
NOSTATIC_RESERVE(iadr, char, HTS_URLMAXSIZE*2);
// sauter user:pass@ Θventuel
strcpy(iadr,jump_identification(_iadr));
// couper Θventuel :
{
char *a;
if ( (a=jump_toport(iadr)) )
*a='\0';
}
#if HTS_WIN
if (inet_addr(iadr)!=INADDR_NONE) // numΘrique
#else
if (inet_addr(iadr)!=(in_addr_t) -1 ) // numΘrique
#endif
return 1;
while(_hts_lockdns(-1)); // attendre libΘration
_hts_lockdns(1); // locker
while(1) {
if (strcmp(cache->iadr,iadr)==0) { // ok trouvΘ
_hts_lockdns(0); // dΘlocker
return 1; // prΘsent!
} else { // on a pas encore trouvΘ
if (cache->n!=NULL) { // chercher encore
cache=cache->n; // suivant!
} else {
_hts_lockdns(0); // dΘlocker
return 2; // non prΘsent
}
}
}
}
t_hostent* vxgethostbyname(char* hostname, void* v_buffer) {
t_fullhostent* buffer = (t_fullhostent*) v_buffer;
/* Clear */
fullhostent_init(buffer);
/* Protection */
if (!strnotempty(hostname)) {
return NULL;
}
/*
Strip [] if any : [3ffe:b80:1234:1::1]
The resolver doesn't seem to handle IP6 addresses in brackets
*/
if ((hostname[0] == '[') && (hostname[strlen(hostname)-1] == ']')) {
char tempo[HTS_URLMAXSIZE*2];
tempo[0]='\0';
strncat(tempo, hostname+1, strlen(hostname)-2);
strcpy(hostname, tempo);
}
{
#if HTS_INET6==0
/*
ipV4 resolver
*/
t_hostent* hp=gethostbyname(hostname);
if (hp!=NULL) {
if ( (hp->h_length) && ( ((unsigned int) hp->h_length) <= buffer->addr_maxlen) ) {
memcpy(buffer->hp.h_addr_list[0], hp->h_addr_list[0], hp->h_length);
buffer->hp.h_length = hp->h_length;
return &(buffer->hp);
}
}
#else
/*
ipV6 resolver
*/
/*
int error_num=0;
t_hostent* hp=getipnodebyname(hostname, AF_INET6, AI_DEFAULT, &error_num);
oops, deprecated :(
*/
struct addrinfo* res = NULL;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
if (res) {
if ( (res->ai_addr) && (res->ai_addrlen) && (res->ai_addrlen <= buffer->addr_maxlen) ) {
memcpy(buffer->hp.h_addr_list[0], res->ai_addr, res->ai_addrlen);
buffer->hp.h_length = res->ai_addrlen;
return &(buffer->hp);
}
}
}
if (res) {
freeaddrinfo(res);
}
#endif
}
return NULL;
}
// cache dns interne α HTS // ** FREE A FAIRE sur la chaine
t_hostent* hts_gethostbyname(char* _iadr, void* v_buffer) {
char iadr[HTS_URLMAXSIZE*2];
t_fullhostent* buffer = (t_fullhostent*) v_buffer;
t_dnscache* cache=_hts_cache(); // adresse du cache
t_hostent* hp;
/* Clear */
fullhostent_init(buffer);
strcpy(iadr,jump_identification(_iadr));
// couper Θventuel :
{
char *a;
if ( (a=jump_toport(iadr)) )
*a='\0';
}
// effacer structure de retour, crΘer nouvelle
/*
memset(&host, 0, sizeof(t_hostent));
host.h_addr_list=he;
he[0]=NULL;
he[1]=NULL;
host.h_length=0;
*/
cache->iadr[0]='*';
cache->iadr[1]='\0';
/* get IP from the dns cache */
hp = _hts_ghbn(cache, iadr, &buffer->hp);
if (hp) {
if (hp->h_length>0)
return hp;
else
return NULL; // entrΘe erronΘe (erreur DNS) dans le DNS
} else { // non prΘsent dans le cache dns, tester
t_dnscache* c=cache;
while(c->n) c=c->n; // calculer queue
#if HTS_WIDE_DEBUG
DEBUG_W("gethostbyname\n");
#endif
#if HDEBUG
printf("gethostbyname (not in cache)\n");
#endif
{
unsigned long inetaddr;
#if HTS_WIN
if ((inetaddr=inet_addr(iadr))==INADDR_NONE) {
#else
if ((inetaddr=inet_addr(iadr))==(in_addr_t) -1 ) {
#endif
#if DEBUGDNS
printf("resolving (not cached) %s\n",iadr);
#endif
hp=vxgethostbyname(iadr, buffer); // calculer IP host
} else { // numΘrique, convertir sans passer par le dns
buffer->hp.h_addr_list[0]=(char*) &inetaddr;
buffer->hp.h_length=4;
hp=&buffer->hp;
}
}
#if HTS_WIDE_DEBUG
DEBUG_W("gethostbyname done\n");
#endif
cache->n=(t_dnscache*) calloct(1,sizeof(t_dnscache));
if (cache->n!=NULL) {
strcpy(cache->n->iadr,iadr);
if (hp!=NULL) {
memcpy(cache->n->host_addr, hp->h_addr_list[0], hp->h_length);
cache->n->host_length=hp->h_length;
} else {
cache->n->host_addr[0]='\0';
cache->n->host_length=0; // non existant dans le dns
}
cache->n->n=NULL;
return hp;
} else { // on peut pas noter, mais on peut renvoyer le rΘsultat
return hp;
}
} // retour hp du cache
}
#else
HTS_INLINE t_hostent* hts_gethostbyname(char* iadr, t_fullhostent* buffer) {
t_hostent* retour;
#if HTS_WIDE_DEBUG
DEBUG_W("gethostbyname (2)\n");
#endif
#if DEBUGDNS
printf("blocking method gethostbyname() in progress for %s\n",iadr);
#endif
retour=vxgethostbyname(jump_identification(iadr), );
#if HTS_WIDE_DEBUG
DEBUG_W("gethostbyname (2) done\n");
#endif
return retour;
}
#endif
// --- Tracage des mallocs() ---
#if HTS_TRACE_MALLOC
typedef struct _mlink {
void* adr;
int len;
int id;
struct _mlink* next;
} mlink;
mlink trmalloc = {NULL,0,0,NULL};
int trmalloc_id=0;
HTS_INLINE void* hts_malloc(size_t len,size_t len2) {
mlink* lnk = (mlink*) calloc(1,sizeof(mlink));
void* r = NULL;
if (lnk) {
if (len2)
r = calloc(len,len2);
else
r = malloc(len);
if (r) {
lnk->adr=r;
if (len2)
lnk->len=len*len2;
else
lnk->len=len;
lnk->id=trmalloc_id++;
lnk->next=trmalloc.next;
trmalloc.next=lnk;
#if MEMDEBUG
//printf("malloc: %d\n",r);
#endif
} else free(lnk);
}
return r;
}
HTS_INLINE void hts_free(void* adr) {
mlink* lnk = &trmalloc;
if (!adr) {
#if MEMDEBUG
printf("* unexpected free() error at %d\n",adr);
#endif
return;
}
do {
if (lnk->next->adr==adr) {
mlink* blk_free=lnk->next;
#if 1
lnk->next=lnk->next->next;
free((void*) blk_free);
#else
#if MEMDEBUG
if (blk_free->id==-1) {
printf("* memory has already been freed: %d (id=%d)\n",blk_free->adr,blk_free->id);
}
#endif
blk_free->id=-1;
#endif
free(adr);
#if MEMDEBUG
//printf("free: %d (id=%d)\n",blk_free->adr,blk_free->id);
#endif
return;
}
lnk=lnk->next;
} while(lnk->next != NULL);
#if MEMDEBUG
printf("* unexpected free() error at %d\n",adr);
#endif
free(adr);
}
HTS_INLINE void* hts_realloc(void* adr,size_t len) {
mlink* lnk = &trmalloc;
do {
if (lnk->next->adr==adr) {
adr = realloc(adr,len);
lnk->next->adr = adr;
lnk->next->len = len;
#if MEMDEBUG
//printf("realloc: %d (id=%d)\n",lnk->next->adr,lnk->next->id);
#endif
return adr;
}
lnk=lnk->next;
} while(lnk->next != NULL);
#if MEMDEBUG
printf("* unexpected realloc() error at %d\n",adr);
#endif
return realloc(adr,len);
}
// check the malloct() and calloct() trace stack
void hts_freeall(void) {
while(trmalloc.next) {
#if MEMDEBUG
printf("* block %d\t not released: at %d\t (%d\t bytes)\n",trmalloc.next->id,trmalloc.next->adr,trmalloc.next->len);
#endif
if (trmalloc.next->id != -1) {
freet(trmalloc.next->adr);
}
}
}
#endif
// -- divers //
// cut path and project name
// patch also initial path
void cut_path(char* fullpath,char* path,char* pname) {
path[0]=pname[0]='\0';
if (strnotempty(fullpath)) {
if ((fullpath[strlen(fullpath)-1]=='/') || (fullpath[strlen(fullpath)-1]=='\\'))
fullpath[strlen(fullpath)-1]='\0';
if (strlen(fullpath)>1) {
char* a;
while( (a=strchr(fullpath,'\\')) ) *a='/'; // remplacer par /
a=fullpath+strlen(fullpath)-2;
while( (*a!='/') && ( a > fullpath)) a--;
if (*a=='/') a++;
strcpy(pname,a);
strncat(path,fullpath,(int) (a - fullpath));
}
}
}
// -- Gestion protocole ftp --
#if HTS_WIN
int ftp_available(void) {
return 1;
}
#else
int ftp_available(void) {
return 1; // ok!
//return 0; // SOUS UNIX, PROBLEMES
}
#endif
int hts_init(void) {
static int hts_init_ok = 0;
if (!hts_init_ok) {
hts_init_ok = 1;
// default wrappers
htswrap_init();
htswrap_add("init",htsdefault_init);
htswrap_add("free",htsdefault_uninit);
htswrap_add("start",htsdefault_start);
htswrap_add("change-options",htsdefault_chopt);
htswrap_add("end",htsdefault_end);
htswrap_add("check-html",htsdefault_checkhtml);
htswrap_add("loop",htsdefault_loop);
htswrap_add("query",htsdefault_query);
htswrap_add("query2",htsdefault_query2);
htswrap_add("query3",htsdefault_query3);
htswrap_add("check-link",htsdefault_check);
htswrap_add("pause",htsdefault_pause);
htswrap_add("save-file",htsdefault_filesave);
htswrap_add("link-detected",htsdefault_linkdetected);
htswrap_add("transfer-status",htsdefault_xfrstatus);
htswrap_add("save-name",htsdefault_savename);
}
#if HTS_USEOPENSSL
/*
Initialize the OpensSSL library
*/
if (!openssl_ctx) {
SSL_library_init();
SSL_load_error_strings();
ERR_load_crypto_strings();
ERR_load_SSL_strings();
SSLeay_add_ssl_algorithms();
// OpenSSL_add_all_algorithms();
openssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!openssl_ctx) {
fprintf(stderr, "fatal: unable to initialize TLS: SSL_CTX_new(SSLv23_client_method)\n");
abort();
}
}
#endif
/* Init vars and thread-specific values */
hts_initvar();
return 1;
}
int hts_uninit(void) {
hts_freevar();
/* htswrap_free(); */
return 1;
}
// defaut wrappers
void __cdecl htsdefault_init(void) {
}
void __cdecl htsdefault_uninit(void) {
hts_freevar();
}
int __cdecl htsdefault_start(void* opt) {
return 1;
}
int __cdecl htsdefault_chopt(void* opt) {
return 1;
}
int __cdecl htsdefault_end(void) {
return 1;
}
int __cdecl htsdefault_checkhtml(char* html,int len,char* url_adresse,char* url_fichier) {
return 1;
}
int __cdecl htsdefault_loop(void* back,int back_max,int back_index,int lien_n,int lien_tot,int stat_time,hts_stat_struct* stats) { // appelΘ α chaque boucle de HTTrack
return 1;
}
char* __cdecl htsdefault_query(char* question) {
return "";
}
char* __cdecl htsdefault_query2(char* question) {
return "";
}
char* __cdecl htsdefault_query3(char* question) {
return "";
}
int __cdecl htsdefault_check(char* adr,char* fil,int status) {
return -1;
}
void __cdecl htsdefault_pause(char* lockfile) {
while (fexist(lockfile)) {
Sleep(1000);
}
}
void __cdecl htsdefault_filesave(char* file) {
}
int __cdecl htsdefault_linkdetected(char* link) {
return 1;
}
int __cdecl htsdefault_xfrstatus(void* back) {
return 1;
}
int __cdecl htsdefault_savename(char* adr_complete,char* fil_complete,char* referer_adr,char* referer_fil,char* save) {
return 1;
}
// end defaut wrappers
// Fin